Update.
[glibc.git] / nis / nis_call.c
blob94144d50eac0ac756b838685800b60e6c7e768f8
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 static void
64 __bind_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 static nis_error
75 __bind_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 (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].proto,
95 "-") == 0)
97 bind->current_ep = j;
98 return NIS_SUCCESS;
101 ++bind->trys;
102 ++bind->server_used;
103 if (bind->server_used >= bind->server_len)
104 bind->server_used = 0;
106 for (j = 0; j < bind->server_val[bind->server_used].ep.ep_len; ++j)
107 if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
108 "inet") == 0)
109 if (bind->server_val[bind->server_used].ep.ep_val[j].proto[0] == '-')
111 bind->current_ep = j;
112 return NIS_SUCCESS;
115 return NIS_FAIL;
118 static nis_error
119 __bind_connect (dir_binding *dbp)
121 nis_server *serv;
123 if (dbp == NULL)
124 return NIS_FAIL;
126 serv = &dbp->server_val[dbp->server_used];
128 memset (&dbp->addr, '\0', sizeof (dbp->addr));
129 dbp->addr.sin_family = AF_INET;
131 dbp->addr.sin_addr.s_addr =
132 inetstr2int (serv->ep.ep_val[dbp->current_ep].uaddr);
134 if (dbp->addr.sin_addr.s_addr == 0)
135 return NIS_FAIL;
137 /* Check, if the host is online and rpc.nisd is running. Much faster
138 then the clnt*_create functions: */
139 if (__pmap_getnisport (&dbp->addr, NIS_PROG, NIS_VERSION, IPPROTO_UDP) == 0)
140 return NIS_RPCERROR;
142 dbp->socket = RPC_ANYSOCK;
143 if (dbp->use_udp)
144 dbp->clnt = clntudp_create (&dbp->addr, NIS_PROG, NIS_VERSION,
145 UDPTIMEOUT, &dbp->socket);
146 else
147 dbp->clnt = clnttcp_create (&dbp->addr, NIS_PROG, NIS_VERSION,
148 &dbp->socket, 0, 0);
150 if (dbp->clnt == NULL)
151 return NIS_RPCERROR;
153 clnt_control (dbp->clnt, CLSET_TIMEOUT, (caddr_t)&RPCTIMEOUT);
154 /* If the program exists, close the socket */
155 if (fcntl (dbp->socket, F_SETFD, 1) == -1)
156 perror (_("fcntl: F_SETFD"));
158 if (dbp->use_auth)
160 if (serv->key_type == NIS_PK_DH && key_secretkey_is_set ())
162 char netname[MAXNETNAMELEN+1];
163 char *p;
165 p = stpcpy (netname, "unix.");
166 strncpy (p, serv->name,MAXNETNAMELEN-5);
167 netname[MAXNETNAMELEN] = '\0';
168 p = strchr (netname, '.');
169 *p = '@';
170 dbp->clnt->cl_auth =
171 authdes_pk_create (netname, &serv->pkey, 300, NULL, NULL);
172 if (!dbp->clnt->cl_auth)
173 dbp->clnt->cl_auth = authunix_create_default ();
175 else
176 dbp->clnt->cl_auth = authunix_create_default ();
177 dbp->use_auth = TRUE;
180 return NIS_SUCCESS;
183 static nis_error
184 __bind_create (dir_binding *dbp, const nis_server *serv_val, u_int serv_len,
185 u_long flags, cache2_info *cinfo)
187 dbp->clnt = NULL;
189 dbp->server_len = serv_len;
190 dbp->server_val = (nis_server *)serv_val;
192 if (flags & USE_DGRAM)
193 dbp->use_udp = TRUE;
194 else
195 dbp->use_udp = FALSE;
197 if (flags & NO_AUTHINFO)
198 dbp->use_auth = FALSE;
199 else
200 dbp->use_auth = TRUE;
202 if (flags & MASTER_ONLY)
203 dbp->master_only = TRUE;
204 else
205 dbp->master_only = FALSE;
207 /* We try the first server */
208 dbp->trys = 1;
210 dbp->class = -1;
211 if (cinfo != NULL && cinfo->server_used >= 0)
213 dbp->server_used = cinfo->server_used;
214 dbp->current_ep = cinfo->current_ep;
215 dbp->class = cinfo->class;
217 else if (__nis_findfastest (dbp) < 1)
219 __bind_destroy (dbp);
220 return NIS_NAMEUNREACHABLE;
223 return NIS_SUCCESS;
226 nis_error
227 __do_niscall2 (const nis_server *server, u_int server_len, u_long prog,
228 xdrproc_t xargs, caddr_t req, xdrproc_t xres, caddr_t resp,
229 u_long flags, nis_cb *cb, cache2_info *cinfo)
231 enum clnt_stat result;
232 nis_error retcode;
233 dir_binding dbp;
235 if (flags & MASTER_ONLY)
236 server_len = 1;
238 if (__bind_create (&dbp, server, server_len, flags, cinfo) != NIS_SUCCESS)
239 return NIS_NAMEUNREACHABLE;
240 while (__bind_connect (&dbp) != NIS_SUCCESS)
242 if (__bind_next (&dbp) != NIS_SUCCESS)
244 __bind_destroy (&dbp);
245 return NIS_NAMEUNREACHABLE;
251 again:
252 result = clnt_call (dbp.clnt, prog, xargs, req, xres, resp, RPCTIMEOUT);
254 if (result != RPC_SUCCESS)
256 __bind_destroy (&dbp);
257 retcode = NIS_RPCERROR;
259 else
261 switch (prog)
263 case NIS_IBLIST:
264 if ((((nis_result *)resp)->status == NIS_CBRESULTS) &&
265 (cb != NULL))
267 __nis_do_callback(&dbp, &((nis_result *)resp)->cookie, cb);
268 break;
270 /* Yes, this is correct. If we doesn't have to start
271 a callback, look if we have to search another server */
272 case NIS_LOOKUP:
273 case NIS_ADD:
274 case NIS_MODIFY:
275 case NIS_REMOVE:
276 case NIS_IBADD:
277 case NIS_IBMODIFY:
278 case NIS_IBREMOVE:
279 case NIS_IBFIRST:
280 case NIS_IBNEXT:
281 if ((((nis_result *)resp)->status == NIS_NOTFOUND) ||
282 (((nis_result *)resp)->status == NIS_NOSUCHNAME) ||
283 (((nis_result *)resp)->status == NIS_NOT_ME))
285 if (__bind_next (&dbp) == NIS_SUCCESS)
287 while (__bind_connect (&dbp) != NIS_SUCCESS)
289 if (__bind_next (&dbp) != NIS_SUCCESS)
291 __bind_destroy (&dbp);
292 return NIS_SUCCESS;
296 else
297 break; /* No more servers to search in */
298 goto again;
300 break;
301 case NIS_FINDDIRECTORY:
302 if ((((fd_result *)resp)->status == NIS_NOTFOUND) ||
303 (((fd_result *)resp)->status == NIS_NOSUCHNAME) ||
304 (((fd_result *)resp)->status == NIS_NOT_ME))
306 if (__bind_next (&dbp) == NIS_SUCCESS)
308 while (__bind_connect (&dbp) != NIS_SUCCESS)
310 if (__bind_next (&dbp) != NIS_SUCCESS)
312 __bind_destroy (&dbp);
313 return NIS_SUCCESS;
317 else
318 break; /* No more servers to search in */
319 goto again;
321 break;
322 case NIS_DUMPLOG: /* log_result */
323 case NIS_DUMP:
324 if ((((log_result *)resp)->lr_status == NIS_NOTFOUND) ||
325 (((log_result *)resp)->lr_status == NIS_NOSUCHNAME) ||
326 (((log_result *)resp)->lr_status == NIS_NOT_ME))
328 if (__bind_next (&dbp) == NIS_SUCCESS)
330 while (__bind_connect (&dbp) != NIS_SUCCESS)
332 if (__bind_next (&dbp) != NIS_SUCCESS)
334 __bind_destroy (&dbp);
335 return NIS_SUCCESS;
339 else
340 break; /* No more servers to search in */
341 goto again;
343 break;
344 default:
345 break;
347 __bind_destroy (&dbp);
348 retcode = NIS_SUCCESS;
351 while ((flags & HARD_LOOKUP) && retcode == NIS_RPCERROR);
353 return retcode;
356 static directory_obj *
357 rec_dirsearch (const_nis_name name, directory_obj *dir, u_long flags,
358 nis_error *status)
360 fd_result *fd_res;
361 XDR xdrs;
363 switch (nis_dir_cmp (name, dir->do_name))
365 case SAME_NAME:
366 *status = NIS_SUCCESS;
367 return dir;
368 case NOT_SEQUENTIAL:
369 /* NOT_SEQUENTIAL means, go one up and try it there ! */
370 case HIGHER_NAME:
371 { /* We need data from a parent domain */
372 directory_obj *obj;
373 char ndomain [strlen (name) + 3];
375 nis_domain_of_r (dir->do_name, ndomain, sizeof (ndomain));
377 /* The root server of our domain is a replica of the parent
378 domain ! (Now I understand why a root server must be a
379 replica of the parent domain) */
380 fd_res = __nis_finddirectory (dir, ndomain);
381 *status = fd_res->status;
382 if (fd_res->status != NIS_SUCCESS)
384 /* Try the current directory obj, maybe it works */
385 __free_fdresult (fd_res);
386 return dir;
388 obj = calloc(1, sizeof(directory_obj));
389 xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
390 fd_res->dir_data.dir_data_len, XDR_DECODE);
391 _xdr_directory_obj(&xdrs, obj);
392 xdr_destroy(&xdrs);
393 __free_fdresult (fd_res);
394 if (obj != NULL)
396 /* We have found a NIS+ server serving ndomain, now
397 let us search for "name" */
398 nis_free_directory (dir);
399 return rec_dirsearch (name, obj, flags, status);
401 else
403 /* Ups, very bad. Are we already the root server ? */
404 nis_free_directory (dir);
405 return NULL;
408 break;
409 case LOWER_NAME:
411 directory_obj *obj;
412 size_t namelen = strlen (name);
413 char leaf [namelen + 3];
414 char domain [namelen + 3];
415 char ndomain [namelen + 3];
416 char *cp;
417 u_int run = 0;
419 strcpy (domain, name);
423 if (domain[0] == '\0')
425 nis_free_directory (dir);
426 return NULL;
428 nis_leaf_of_r (domain, leaf, sizeof (leaf));
429 nis_domain_of_r (domain, ndomain, sizeof (ndomain));
430 strcpy (domain, ndomain);
431 ++run;
433 while (nis_dir_cmp (domain, dir->do_name) != SAME_NAME);
435 if (run == 1)
437 /* We have found the directory above. Use it. */
438 return dir;
441 cp = strchr (leaf, '\0');
442 *cp++ = '.';
443 strcpy (cp, domain);
445 fd_res = __nis_finddirectory (dir, leaf);
446 *status = fd_res->status;
447 if (fd_res->status != NIS_SUCCESS)
449 /* Try the current directory object, maybe it works */
450 __free_fdresult (fd_res);
451 return dir;
453 obj = calloc(1, sizeof(directory_obj));
454 xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
455 fd_res->dir_data.dir_data_len, XDR_DECODE);
456 _xdr_directory_obj(&xdrs, obj);
457 xdr_destroy(&xdrs);
458 __free_fdresult (fd_res);
459 if (obj != NULL)
461 /* We have found a NIS+ server serving ndomain, now
462 let us search for "name" */
463 nis_free_directory (dir);
464 return rec_dirsearch (name, obj, flags, status);
467 break;
468 case BAD_NAME:
469 nis_free_directory (dir);
470 *status = NIS_BADNAME;
471 return NULL;
473 nis_free_directory (dir);
474 *status = NIS_FAIL;
475 return NULL;
478 /* We try to query the current server for the searched object,
479 maybe he know about it ? */
480 static directory_obj *
481 first_shoot (const_nis_name name, directory_obj *dir, u_long flags)
483 directory_obj *obj;
484 fd_result *fd_res;
485 XDR xdrs;
486 char domain [strlen (name) + 3];
488 if (nis_dir_cmp (name, dir->do_name) == SAME_NAME)
489 return dir;
491 nis_domain_of_r (name, domain, sizeof (domain));
493 if (nis_dir_cmp (domain, dir->do_name) == SAME_NAME)
494 return dir;
496 fd_res = __nis_finddirectory (dir, domain);
497 if (fd_res->status != NIS_SUCCESS)
499 __free_fdresult (fd_res);
500 return NULL;
502 obj = calloc(1, sizeof(directory_obj));
503 if (obj == NULL)
504 return NULL;
505 xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
506 fd_res->dir_data.dir_data_len, XDR_DECODE);
507 _xdr_directory_obj (&xdrs, obj);
508 xdr_destroy (&xdrs);
509 __free_fdresult (fd_res);
510 if (obj != NULL)
512 nis_free_directory (dir);
513 return obj;
515 return NULL;
518 nis_error
519 __do_niscall (const_nis_name name, u_long prog, xdrproc_t xargs,
520 caddr_t req, xdrproc_t xres, caddr_t resp, u_long flags,
521 nis_cb *cb)
523 nis_error retcode;
524 directory_obj *dir = NULL;
525 nis_server *server;
526 u_int server_len;
527 cache2_info cinfo = {-1, -1, -1};
528 int saved_errno = errno;
530 if (name == NULL)
531 return NIS_BADNAME;
533 /* Search in local cache. In the moment, we ignore the fastest server */
534 if (!(flags & NO_CACHE))
535 dir = __nis_cache_search (name, flags, &cinfo);
537 if (dir == NULL)
539 nis_error status;
540 directory_obj *obj;
542 dir = readColdStartFile ();
543 if (dir == NULL) /* No /var/nis/NIS_COLD_START->no NIS+ installed */
545 __set_errno (saved_errno);
546 return NIS_UNAVAIL;
549 /* Try at first, if servers in "dir" know our object */
550 obj = first_shoot (name, dir, flags);
551 if (obj == NULL)
553 dir = rec_dirsearch (name, dir, flags, &status);
554 if (dir == NULL)
556 __set_errno (saved_errno);
557 return status;
560 else
561 dir = obj;
564 if (flags & MASTER_ONLY)
566 server = dir->do_servers.do_servers_val;
567 server_len = 1;
569 else
571 server = dir->do_servers.do_servers_val;
572 server_len = dir->do_servers.do_servers_len;
576 retcode = __do_niscall2 (server, server_len, prog, xargs, req, xres, resp,
577 flags, cb, &cinfo);
579 nis_free_directory (dir);
581 __set_errno (saved_errno);
583 return retcode;