2.3.4-2.fc3.6
[glibc.git] / nis / nis_call.c
blob14041a160c40cd8ce4e87dfa2970274c41af260e
1 /* Copyright (C) 1997, 1998, 2001, 2004 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"
34 static const struct timeval RPCTIMEOUT = {10, 0};
35 static const struct timeval UDPTIMEOUT = {5, 0};
37 extern u_short __pmap_getnisport (struct sockaddr_in *address, u_long program,
38 u_long version, u_int protocol);
40 unsigned long
41 inetstr2int (const char *str)
43 char buffer[strlen (str) + 3];
44 size_t buflen;
45 size_t i, j;
47 buflen = stpcpy (buffer, str) - buffer;
49 j = 0;
50 for (i = 0; i < buflen; ++i)
51 if (buffer[i] == '.')
53 ++j;
54 if (j == 4)
56 buffer[i] = '\0';
57 break;
61 return inet_addr (buffer);
64 void
65 __nisbind_destroy (dir_binding *bind)
67 if (bind->clnt != NULL)
69 if (bind->use_auth)
70 auth_destroy (bind->clnt->cl_auth);
71 clnt_destroy (bind->clnt);
74 libnsl_hidden_def (__nisbind_destroy)
76 nis_error
77 __nisbind_next (dir_binding *bind)
79 u_int j;
81 if (bind->clnt != NULL)
83 if (bind->use_auth)
84 auth_destroy (bind->clnt->cl_auth);
85 clnt_destroy (bind->clnt);
86 bind->clnt = NULL;
89 if (bind->trys >= bind->server_len)
90 return NIS_FAIL;
92 for (j = bind->current_ep + 1;
93 j < bind->server_val[bind->server_used].ep.ep_len; ++j)
94 if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
95 "inet") == 0)
96 if (bind->server_val[bind->server_used].ep.ep_val[j].proto[0] == '-')
98 bind->current_ep = j;
99 return NIS_SUCCESS;
102 ++bind->trys;
103 ++bind->server_used;
104 if (bind->server_used >= bind->server_len)
105 bind->server_used = 0;
107 for (j = 0; j < bind->server_val[bind->server_used].ep.ep_len; ++j)
108 if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
109 "inet") == 0)
110 if (bind->server_val[bind->server_used].ep.ep_val[j].proto[0] == '-')
112 bind->current_ep = j;
113 return NIS_SUCCESS;
116 return NIS_FAIL;
118 libnsl_hidden_def (__nisbind_next)
120 nis_error
121 __nisbind_connect (dir_binding *dbp)
123 nis_server *serv;
125 if (dbp == NULL)
126 return NIS_FAIL;
128 serv = &dbp->server_val[dbp->server_used];
130 memset (&dbp->addr, '\0', sizeof (dbp->addr));
131 dbp->addr.sin_family = AF_INET;
133 dbp->addr.sin_addr.s_addr =
134 inetstr2int (serv->ep.ep_val[dbp->current_ep].uaddr);
136 if (dbp->addr.sin_addr.s_addr == 0)
137 return NIS_FAIL;
139 /* Check, if the host is online and rpc.nisd is running. Much faster
140 then the clnt*_create functions: */
141 if (__pmap_getnisport (&dbp->addr, NIS_PROG, NIS_VERSION, IPPROTO_UDP) == 0)
142 return NIS_RPCERROR;
144 dbp->socket = RPC_ANYSOCK;
145 if (dbp->use_udp)
146 dbp->clnt = clntudp_create (&dbp->addr, NIS_PROG, NIS_VERSION,
147 UDPTIMEOUT, &dbp->socket);
148 else
149 dbp->clnt = clnttcp_create (&dbp->addr, NIS_PROG, NIS_VERSION,
150 &dbp->socket, 0, 0);
152 if (dbp->clnt == NULL)
153 return NIS_RPCERROR;
155 clnt_control (dbp->clnt, CLSET_TIMEOUT, (caddr_t) &RPCTIMEOUT);
156 /* If the program exists, close the socket */
157 if (fcntl (dbp->socket, F_SETFD, 1) == -1)
158 perror ("fcntl: F_SETFD");
160 if (dbp->use_auth)
162 if (serv->key_type == NIS_PK_DH)
164 char netname[MAXNETNAMELEN + 1];
165 char *p;
167 p = stpcpy (netname, "unix.");
168 strncpy (p, serv->name, MAXNETNAMELEN - 5);
169 netname[MAXNETNAMELEN] = '\0';
170 // XXX What is this supposed to do? If we really want to replace
171 // XXX the first dot, then we might as well use unix@ as the
172 // XXX prefix string. --drepper
173 p = strchr (netname, '.');
174 *p = '@';
175 dbp->clnt->cl_auth =
176 authdes_pk_create (netname, &serv->pkey, 300, NULL, NULL);
177 if (!dbp->clnt->cl_auth)
178 dbp->clnt->cl_auth = authunix_create_default ();
180 else
181 dbp->clnt->cl_auth = authunix_create_default ();
182 dbp->use_auth = TRUE;
185 return NIS_SUCCESS;
187 libnsl_hidden_def (__nisbind_connect)
189 nis_error
190 __nisbind_create (dir_binding *dbp, const nis_server *serv_val,
191 unsigned int serv_len, unsigned int flags)
193 dbp->clnt = NULL;
195 dbp->server_len = serv_len;
196 dbp->server_val = (nis_server *)serv_val;
198 if (flags & USE_DGRAM)
199 dbp->use_udp = TRUE;
200 else
201 dbp->use_udp = FALSE;
203 if (flags & NO_AUTHINFO)
204 dbp->use_auth = FALSE;
205 else
206 dbp->use_auth = TRUE;
208 if (flags & MASTER_ONLY)
209 dbp->master_only = TRUE;
210 else
211 dbp->master_only = FALSE;
213 /* We try the first server */
214 dbp->trys = 1;
216 dbp->class = -1;
217 if (__nis_findfastest (dbp) < 1)
219 __nisbind_destroy (dbp);
220 return NIS_NAMEUNREACHABLE;
223 return NIS_SUCCESS;
225 libnsl_hidden_def (__nisbind_create)
227 /* __nisbind_connect (dbp) must be run before calling this function !
228 So we could use the same binding twice */
229 nis_error
230 __do_niscall3 (dir_binding *dbp, u_long prog, xdrproc_t xargs, caddr_t req,
231 xdrproc_t xres, caddr_t resp, unsigned int flags, nis_cb *cb)
233 enum clnt_stat result;
234 nis_error retcode;
236 if (dbp == NULL)
237 return NIS_NAMEUNREACHABLE;
241 again:
242 result = clnt_call (dbp->clnt, prog, xargs, req, xres, resp, RPCTIMEOUT);
244 if (result != RPC_SUCCESS)
245 retcode = NIS_RPCERROR;
246 else
248 switch (prog)
250 case NIS_IBLIST:
251 if ((((nis_result *)resp)->status == NIS_CBRESULTS) &&
252 (cb != NULL))
254 __nis_do_callback (dbp, &((nis_result *) resp)->cookie, cb);
255 break;
257 /* Yes, the missing break is correct. If we doesn't have to
258 start a callback, look if we have to search another server */
259 case NIS_LOOKUP:
260 case NIS_ADD:
261 case NIS_MODIFY:
262 case NIS_REMOVE:
263 case NIS_IBADD:
264 case NIS_IBMODIFY:
265 case NIS_IBREMOVE:
266 case NIS_IBFIRST:
267 case NIS_IBNEXT:
268 if (((nis_result *)resp)->status == NIS_SYSTEMERROR
269 || ((nis_result *)resp)->status == NIS_NOSUCHNAME
270 || ((nis_result *)resp)->status == NIS_NOT_ME)
272 if (__nisbind_next (dbp) == NIS_SUCCESS)
274 while (__nisbind_connect (dbp) != NIS_SUCCESS)
276 if (__nisbind_next (dbp) != NIS_SUCCESS)
277 return NIS_SUCCESS;
280 else
281 break; /* No more servers to search in */
282 goto again;
284 break;
285 case NIS_FINDDIRECTORY:
286 if (((fd_result *)resp)->status == NIS_SYSTEMERROR
287 || ((fd_result *)resp)->status == NIS_NOSUCHNAME
288 || ((fd_result *)resp)->status == NIS_NOT_ME)
290 if (__nisbind_next (dbp) == NIS_SUCCESS)
292 while (__nisbind_connect (dbp) != NIS_SUCCESS)
294 if (__nisbind_next (dbp) != NIS_SUCCESS)
295 return NIS_SUCCESS;
298 else
299 break; /* No more servers to search in */
300 goto again;
302 break;
303 case NIS_DUMPLOG: /* log_result */
304 case NIS_DUMP:
305 if (((log_result *)resp)->lr_status == NIS_SYSTEMERROR
306 || ((log_result *)resp)->lr_status == NIS_NOSUCHNAME
307 || ((log_result *)resp)->lr_status == NIS_NOT_ME)
309 if (__nisbind_next (dbp) == NIS_SUCCESS)
311 while (__nisbind_connect (dbp) != NIS_SUCCESS)
313 if (__nisbind_next (dbp) != NIS_SUCCESS)
314 return NIS_SUCCESS;
317 else
318 break; /* No more servers to search in */
319 goto again;
321 break;
322 default:
323 break;
325 retcode = NIS_SUCCESS;
328 while ((flags & HARD_LOOKUP) && retcode == NIS_RPCERROR);
330 return retcode;
333 nis_error
334 __do_niscall2 (const nis_server *server, u_int server_len, u_long prog,
335 xdrproc_t xargs, caddr_t req, xdrproc_t xres, caddr_t resp,
336 unsigned int flags, nis_cb *cb)
338 dir_binding dbp;
339 nis_error status;
341 if (flags & MASTER_ONLY)
342 server_len = 1;
344 status = __nisbind_create (&dbp, server, server_len, flags);
345 if (status != NIS_SUCCESS)
346 return status;
348 while (__nisbind_connect (&dbp) != NIS_SUCCESS)
349 if (__nisbind_next (&dbp) != NIS_SUCCESS)
350 return NIS_NAMEUNREACHABLE;
352 status = __do_niscall3 (&dbp, prog, xargs, req, xres, resp, flags, cb);
354 __nisbind_destroy (&dbp);
356 return status;
360 static directory_obj *
361 rec_dirsearch (const_nis_name name, directory_obj *dir, nis_error *status)
363 fd_result *fd_res;
364 XDR xdrs;
366 switch (nis_dir_cmp (name, dir->do_name))
368 case SAME_NAME:
369 *status = NIS_SUCCESS;
370 return dir;
371 case NOT_SEQUENTIAL:
372 /* NOT_SEQUENTIAL means, go one up and try it there ! */
373 case HIGHER_NAME:
374 { /* We need data from a parent domain */
375 directory_obj *obj;
376 char ndomain [strlen (name) + 3];
378 nis_domain_of_r (dir->do_name, ndomain, sizeof (ndomain));
380 /* The root server of our domain is a replica of the parent
381 domain ! (Now I understand why a root server must be a
382 replica of the parent domain) */
383 fd_res = __nis_finddirectory (dir, ndomain);
384 *status = fd_res->status;
385 if (fd_res->status != NIS_SUCCESS)
387 /* Try the current directory obj, maybe it works */
388 __free_fdresult (fd_res);
389 return dir;
391 obj = calloc (1, sizeof (directory_obj));
392 xdrmem_create (&xdrs, fd_res->dir_data.dir_data_val,
393 fd_res->dir_data.dir_data_len, XDR_DECODE);
394 _xdr_directory_obj (&xdrs, obj);
395 xdr_destroy (&xdrs);
396 __free_fdresult (fd_res);
397 if (obj != NULL)
399 /* We have found a NIS+ server serving ndomain, now
400 let us search for "name" */
401 nis_free_directory (dir);
402 return rec_dirsearch (name, obj, status);
404 else
406 /* Ups, very bad. Are we already the root server ? */
407 nis_free_directory (dir);
408 return NULL;
411 break;
412 case LOWER_NAME:
414 directory_obj *obj;
415 size_t namelen = strlen (name);
416 char leaf[namelen + 3];
417 char domain[namelen + 3];
418 char ndomain[namelen + 3];
419 char *cp;
420 u_int run = 0;
422 strcpy (domain, name);
426 if (domain[0] == '\0')
428 nis_free_directory (dir);
429 return NULL;
431 nis_leaf_of_r (domain, leaf, sizeof (leaf));
432 nis_domain_of_r (domain, ndomain, sizeof (ndomain));
433 strcpy (domain, ndomain);
434 ++run;
436 while (nis_dir_cmp (domain, dir->do_name) != SAME_NAME);
438 if (run == 1)
440 /* We have found the directory above. Use it. */
441 return dir;
444 cp = strchr (leaf, '\0');
445 *cp++ = '.';
446 strcpy (cp, domain);
448 fd_res = __nis_finddirectory (dir, leaf);
449 *status = fd_res->status;
450 if (fd_res->status != NIS_SUCCESS)
452 /* Try the current directory object, maybe it works */
453 __free_fdresult (fd_res);
454 return dir;
456 obj = calloc(1, sizeof(directory_obj));
457 xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
458 fd_res->dir_data.dir_data_len, XDR_DECODE);
459 _xdr_directory_obj(&xdrs, obj);
460 xdr_destroy(&xdrs);
461 __free_fdresult (fd_res);
462 if (obj != NULL)
464 /* We have found a NIS+ server serving ndomain, now
465 let us search for "name" */
466 nis_free_directory (dir);
467 return rec_dirsearch (name, obj, status);
470 break;
471 case BAD_NAME:
472 nis_free_directory (dir);
473 *status = NIS_BADNAME;
474 return NULL;
476 nis_free_directory (dir);
477 *status = NIS_FAIL;
478 return NULL;
481 /* We try to query the current server for the searched object,
482 maybe he know about it ? */
483 static directory_obj *
484 first_shoot (const_nis_name name, directory_obj *dir)
486 directory_obj *obj = NULL;
487 fd_result *fd_res;
488 XDR xdrs;
489 char domain[strlen (name) + 3];
491 if (nis_dir_cmp (name, dir->do_name) == SAME_NAME)
492 return dir;
494 nis_domain_of_r (name, domain, sizeof (domain));
496 if (nis_dir_cmp (domain, dir->do_name) == SAME_NAME)
497 return dir;
499 fd_res = __nis_finddirectory (dir, domain);
500 if (fd_res->status == NIS_SUCCESS
501 && (obj = calloc (1, sizeof (directory_obj))) != NULL)
503 xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
504 fd_res->dir_data.dir_data_len, XDR_DECODE);
505 _xdr_directory_obj (&xdrs, obj);
506 xdr_destroy (&xdrs);
509 __free_fdresult (fd_res);
511 if (obj != NULL)
512 nis_free_directory (dir);
514 return obj;
517 nis_error
518 __nisfind_server (const_nis_name name, directory_obj **dir)
520 if (name == NULL)
521 return NIS_BADNAME;
523 #if 0
524 /* Search in local cache. In the moment, we ignore the fastest server */
525 if (!(flags & NO_CACHE))
526 dir = __nis_cache_search (name, flags, &cinfo);
527 #endif
529 if (*dir == NULL)
531 nis_error status;
532 directory_obj *obj;
534 *dir = readColdStartFile ();
535 if (*dir == NULL) /* No /var/nis/NIS_COLD_START->no NIS+ installed */
536 return NIS_UNAVAIL;
538 /* Try at first, if servers in "dir" know our object */
539 obj = first_shoot (name, *dir);
540 if (obj == NULL)
542 *dir = rec_dirsearch (name, *dir, &status);
543 if (*dir == NULL)
544 return status;
546 else
547 *dir = obj;
550 return NIS_SUCCESS;
553 nis_error
554 __do_niscall (const_nis_name name, u_long prog, xdrproc_t xargs,
555 caddr_t req, xdrproc_t xres, caddr_t resp, unsigned int flags,
556 nis_cb *cb)
558 nis_error retcode;
559 dir_binding bptr;
560 directory_obj *dir = NULL;
561 nis_server *server;
562 u_int server_len;
563 int saved_errno = errno;
565 retcode = __nisfind_server (name, &dir);
566 if (retcode != NIS_SUCCESS)
567 return retcode;
569 if (flags & MASTER_ONLY)
571 server = dir->do_servers.do_servers_val;
572 server_len = 1;
574 else
576 server = dir->do_servers.do_servers_val;
577 server_len = dir->do_servers.do_servers_len;
580 retcode = __nisbind_create (&bptr, server, server_len, flags);
581 if (retcode == NIS_SUCCESS)
583 while (__nisbind_connect (&bptr) != NIS_SUCCESS)
585 if (__nisbind_next (&bptr) != NIS_SUCCESS)
587 nis_free_directory (dir);
588 __nisbind_destroy (&bptr);
589 return NIS_NAMEUNREACHABLE;
592 retcode = __do_niscall3 (&bptr, prog, xargs, req, xres, resp, flags, cb);
594 __nisbind_destroy (&bptr);
597 nis_free_directory (dir);
599 __set_errno (saved_errno);
601 return retcode;