* nis/nss_compat/compat-pwd.c (internal_setpwent): If nss_set*ent
[glibc.git] / nis / nis_call.c
blob753ef7799720de72441073ff44fd175d57e856d6
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"
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 size_t j = 0;
44 for (size_t i = 0; str[i] != '\0'; ++i)
45 if (str[i] == '.' && ++j == 4)
47 char buffer[i + 1];
48 buffer[i] = '\0';
49 return inet_addr (memcpy (buffer, str, i));
52 return inet_addr (str);
55 void
56 __nisbind_destroy (dir_binding *bind)
58 if (bind->clnt != NULL)
60 if (bind->use_auth)
61 auth_destroy (bind->clnt->cl_auth);
62 clnt_destroy (bind->clnt);
65 libnsl_hidden_def (__nisbind_destroy)
67 nis_error
68 __nisbind_next (dir_binding *bind)
70 u_int j;
72 if (bind->clnt != NULL)
74 if (bind->use_auth)
75 auth_destroy (bind->clnt->cl_auth);
76 clnt_destroy (bind->clnt);
77 bind->clnt = NULL;
80 if (bind->trys >= bind->server_len)
81 return NIS_FAIL;
83 for (j = bind->current_ep + 1;
84 j < bind->server_val[bind->server_used].ep.ep_len; ++j)
85 if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
86 "inet") == 0)
87 if (bind->server_val[bind->server_used].ep.ep_val[j].proto[0] == '-')
89 bind->current_ep = j;
90 return NIS_SUCCESS;
93 ++bind->trys;
94 ++bind->server_used;
95 if (bind->server_used >= bind->server_len)
96 bind->server_used = 0;
98 for (j = 0; j < bind->server_val[bind->server_used].ep.ep_len; ++j)
99 if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
100 "inet") == 0)
101 if (bind->server_val[bind->server_used].ep.ep_val[j].proto[0] == '-')
103 bind->current_ep = j;
104 return NIS_SUCCESS;
107 return NIS_FAIL;
109 libnsl_hidden_def (__nisbind_next)
111 nis_error
112 __nisbind_connect (dir_binding *dbp)
114 nis_server *serv;
116 if (dbp == NULL)
117 return NIS_FAIL;
119 serv = &dbp->server_val[dbp->server_used];
121 memset (&dbp->addr, '\0', sizeof (dbp->addr));
122 dbp->addr.sin_family = AF_INET;
124 dbp->addr.sin_addr.s_addr =
125 inetstr2int (serv->ep.ep_val[dbp->current_ep].uaddr);
127 if (dbp->addr.sin_addr.s_addr == 0)
128 return NIS_FAIL;
130 /* Check, if the host is online and rpc.nisd is running. Much faster
131 then the clnt*_create functions: */
132 if (__pmap_getnisport (&dbp->addr, NIS_PROG, NIS_VERSION, IPPROTO_UDP) == 0)
133 return NIS_RPCERROR;
135 dbp->socket = RPC_ANYSOCK;
136 if (dbp->use_udp)
137 dbp->clnt = clntudp_create (&dbp->addr, NIS_PROG, NIS_VERSION,
138 UDPTIMEOUT, &dbp->socket);
139 else
140 dbp->clnt = clnttcp_create (&dbp->addr, NIS_PROG, NIS_VERSION,
141 &dbp->socket, 0, 0);
143 if (dbp->clnt == NULL)
144 return NIS_RPCERROR;
146 clnt_control (dbp->clnt, CLSET_TIMEOUT, (caddr_t) &RPCTIMEOUT);
147 /* If the program exists, close the socket */
148 if (fcntl (dbp->socket, F_SETFD, 1) == -1)
149 perror ("fcntl: F_SETFD");
151 if (dbp->use_auth)
153 if (serv->key_type == NIS_PK_DH)
155 char netname[MAXNETNAMELEN + 1];
156 char *p;
158 p = stpcpy (netname, "unix.");
159 strncpy (p, serv->name, MAXNETNAMELEN - 5);
160 netname[MAXNETNAMELEN] = '\0';
161 // XXX What is this supposed to do? If we really want to replace
162 // XXX the first dot, then we might as well use unix@ as the
163 // XXX prefix string. --drepper
164 p = strchr (netname, '.');
165 *p = '@';
166 dbp->clnt->cl_auth =
167 authdes_pk_create (netname, &serv->pkey, 300, NULL, NULL);
168 if (!dbp->clnt->cl_auth)
169 dbp->clnt->cl_auth = authunix_create_default ();
171 else
172 dbp->clnt->cl_auth = authunix_create_default ();
175 return NIS_SUCCESS;
177 libnsl_hidden_def (__nisbind_connect)
179 nis_error
180 __nisbind_create (dir_binding *dbp, const nis_server *serv_val,
181 unsigned int serv_len, unsigned int flags)
183 dbp->clnt = NULL;
185 dbp->server_len = serv_len;
186 dbp->server_val = (nis_server *)serv_val;
188 if (flags & USE_DGRAM)
189 dbp->use_udp = TRUE;
190 else
191 dbp->use_udp = FALSE;
193 if (flags & NO_AUTHINFO)
194 dbp->use_auth = FALSE;
195 else
196 dbp->use_auth = TRUE;
198 if (flags & MASTER_ONLY)
199 dbp->master_only = TRUE;
200 else
201 dbp->master_only = FALSE;
203 /* We try the first server */
204 dbp->trys = 1;
206 dbp->class = -1;
207 if (__nis_findfastest (dbp) < 1)
208 return NIS_NAMEUNREACHABLE;
210 return NIS_SUCCESS;
212 libnsl_hidden_def (__nisbind_create)
214 /* __nisbind_connect (dbp) must be run before calling this function !
215 So we could use the same binding twice */
216 nis_error
217 __do_niscall3 (dir_binding *dbp, u_long prog, xdrproc_t xargs, caddr_t req,
218 xdrproc_t xres, caddr_t resp, unsigned int flags, nis_cb *cb)
220 enum clnt_stat result;
221 nis_error retcode;
223 if (dbp == NULL)
224 return NIS_NAMEUNREACHABLE;
228 again:
229 result = clnt_call (dbp->clnt, prog, xargs, req, xres, resp, RPCTIMEOUT);
231 if (result != RPC_SUCCESS)
232 retcode = NIS_RPCERROR;
233 else
235 switch (prog)
237 case NIS_IBLIST:
238 if ((((nis_result *)resp)->status == NIS_CBRESULTS) &&
239 (cb != NULL))
241 __nis_do_callback (dbp, &((nis_result *) resp)->cookie, cb);
242 break;
244 /* Yes, the missing break is correct. If we doesn't have to
245 start a callback, look if we have to search another server */
246 case NIS_LOOKUP:
247 case NIS_ADD:
248 case NIS_MODIFY:
249 case NIS_REMOVE:
250 case NIS_IBADD:
251 case NIS_IBMODIFY:
252 case NIS_IBREMOVE:
253 case NIS_IBFIRST:
254 case NIS_IBNEXT:
255 if (((nis_result *)resp)->status == NIS_SYSTEMERROR
256 || ((nis_result *)resp)->status == NIS_NOSUCHNAME
257 || ((nis_result *)resp)->status == NIS_NOT_ME)
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)
277 if (__nisbind_next (dbp) == NIS_SUCCESS)
279 while (__nisbind_connect (dbp) != NIS_SUCCESS)
281 if (__nisbind_next (dbp) != NIS_SUCCESS)
282 return NIS_SUCCESS;
285 else
286 break; /* No more servers to search in */
287 goto again;
289 break;
290 case NIS_DUMPLOG: /* log_result */
291 case NIS_DUMP:
292 if (((log_result *)resp)->lr_status == NIS_SYSTEMERROR
293 || ((log_result *)resp)->lr_status == NIS_NOSUCHNAME
294 || ((log_result *)resp)->lr_status == NIS_NOT_ME)
296 if (__nisbind_next (dbp) == NIS_SUCCESS)
298 while (__nisbind_connect (dbp) != NIS_SUCCESS)
300 if (__nisbind_next (dbp) != NIS_SUCCESS)
301 return NIS_SUCCESS;
304 else
305 break; /* No more servers to search in */
306 goto again;
308 break;
309 default:
310 break;
312 retcode = NIS_SUCCESS;
315 while ((flags & HARD_LOOKUP) && retcode == NIS_RPCERROR);
317 return retcode;
320 nis_error
321 __do_niscall2 (const nis_server *server, u_int server_len, u_long prog,
322 xdrproc_t xargs, caddr_t req, xdrproc_t xres, caddr_t resp,
323 unsigned int flags, nis_cb *cb)
325 dir_binding dbp;
326 nis_error status;
328 if (flags & MASTER_ONLY)
329 server_len = 1;
331 status = __nisbind_create (&dbp, server, server_len, flags);
332 if (status != NIS_SUCCESS)
333 return status;
335 while (__nisbind_connect (&dbp) != NIS_SUCCESS)
336 if (__nisbind_next (&dbp) != NIS_SUCCESS)
337 return NIS_NAMEUNREACHABLE;
339 status = __do_niscall3 (&dbp, prog, xargs, req, xres, resp, flags, cb);
341 __nisbind_destroy (&dbp);
343 return status;
347 static directory_obj *
348 rec_dirsearch (const_nis_name name, directory_obj *dir, nis_error *status)
350 fd_result *fd_res;
351 XDR xdrs;
353 switch (nis_dir_cmp (name, dir->do_name))
355 case SAME_NAME:
356 *status = NIS_SUCCESS;
357 return dir;
358 case NOT_SEQUENTIAL:
359 /* NOT_SEQUENTIAL means, go one up and try it there ! */
360 case HIGHER_NAME:
361 { /* We need data from a parent domain */
362 directory_obj *obj;
363 char ndomain [strlen (name) + 3];
365 nis_domain_of_r (dir->do_name, ndomain, sizeof (ndomain));
367 /* The root server of our domain is a replica of the parent
368 domain ! (Now I understand why a root server must be a
369 replica of the parent domain) */
370 fd_res = __nis_finddirectory (dir, ndomain);
371 if (fd_res == NULL)
373 nis_free_directory (dir);
374 *status = NIS_NOMEMORY;
375 return NULL;
377 *status = fd_res->status;
378 if (fd_res->status != NIS_SUCCESS)
380 /* Try the current directory obj, maybe it works */
381 __free_fdresult (fd_res);
382 return dir;
384 nis_free_directory (dir);
385 obj = calloc (1, sizeof (directory_obj));
386 if (obj == NULL)
388 __free_fdresult (fd_res);
389 *status = NIS_NOMEMORY;
390 return NULL;
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);
398 /* We have found a NIS+ server serving ndomain, now
399 let us search for "name" */
400 return rec_dirsearch (name, obj, status);
402 break;
403 case LOWER_NAME:
405 directory_obj *obj;
406 size_t namelen = strlen (name);
407 char leaf[namelen + 3];
408 char domain[namelen + 3];
409 char ndomain[namelen + 3];
410 char *cp;
411 u_int run = 0;
413 strcpy (domain, name);
417 if (domain[0] == '\0')
419 nis_free_directory (dir);
420 return NULL;
422 nis_leaf_of_r (domain, leaf, sizeof (leaf));
423 nis_domain_of_r (domain, ndomain, sizeof (ndomain));
424 strcpy (domain, ndomain);
425 ++run;
427 while (nis_dir_cmp (domain, dir->do_name) != SAME_NAME);
429 if (run == 1)
431 /* We have found the directory above. Use it. */
432 return dir;
435 cp = rawmemchr (leaf, '\0');
436 *cp++ = '.';
437 strcpy (cp, domain);
439 fd_res = __nis_finddirectory (dir, leaf);
440 if (fd_res == NULL)
442 nis_free_directory (dir);
443 *status = NIS_NOMEMORY;
444 return NULL;
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 nis_free_directory (dir);
454 obj = calloc (1, sizeof(directory_obj));
455 if (obj == NULL)
457 __free_fdresult (fd_res);
458 *status = NIS_NOMEMORY;
459 return NULL;
461 xdrmem_create (&xdrs, fd_res->dir_data.dir_data_val,
462 fd_res->dir_data.dir_data_len, XDR_DECODE);
463 _xdr_directory_obj (&xdrs, obj);
464 xdr_destroy (&xdrs);
465 __free_fdresult (fd_res);
466 /* We have found a NIS+ server serving ndomain, now
467 let us search for "name" */
468 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 == NULL)
501 return NULL;
502 if (fd_res->status == NIS_SUCCESS
503 && (obj = calloc (1, sizeof (directory_obj))) != 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);
511 __free_fdresult (fd_res);
513 if (obj != NULL)
514 nis_free_directory (dir);
516 return obj;
519 nis_error
520 __nisfind_server (const_nis_name name, directory_obj **dir)
522 if (name == NULL)
523 return NIS_BADNAME;
525 #if 0
526 /* Search in local cache. In the moment, we ignore the fastest server */
527 if (!(flags & NO_CACHE))
528 dir = __nis_cache_search (name, flags, &cinfo);
529 #endif
531 nis_error result = NIS_SUCCESS;
532 if (*dir == NULL)
534 nis_error status;
535 directory_obj *obj;
537 *dir = readColdStartFile ();
538 if (*dir == NULL)
539 /* No /var/nis/NIS_COLD_START->no NIS+ installed. */
540 return NIS_UNAVAIL;
542 /* Try at first, if servers in "dir" know our object */
543 obj = first_shoot (name, *dir);
544 if (obj == NULL)
546 obj = rec_dirsearch (name, *dir, &status);
547 if (obj == NULL)
548 result = status;
551 *dir = obj;
554 return result;
557 nis_error
558 __do_niscall (const_nis_name name, u_long prog, xdrproc_t xargs,
559 caddr_t req, xdrproc_t xres, caddr_t resp, unsigned int flags,
560 nis_cb *cb)
562 nis_error retcode;
563 dir_binding bptr;
564 directory_obj *dir = NULL;
565 nis_server *server;
566 u_int server_len;
567 int saved_errno = errno;
569 retcode = __nisfind_server (name, &dir);
570 if (retcode != NIS_SUCCESS)
571 return retcode;
573 if (flags & MASTER_ONLY)
575 server = dir->do_servers.do_servers_val;
576 server_len = 1;
578 else
580 server = dir->do_servers.do_servers_val;
581 server_len = dir->do_servers.do_servers_len;
584 retcode = __nisbind_create (&bptr, server, server_len, flags);
585 if (retcode == NIS_SUCCESS)
587 while (__nisbind_connect (&bptr) != NIS_SUCCESS)
589 if (__nisbind_next (&bptr) != NIS_SUCCESS)
591 nis_free_directory (dir);
592 return NIS_NAMEUNREACHABLE;
595 retcode = __do_niscall3 (&bptr, prog, xargs, req, xres, resp, flags, cb);
597 __nisbind_destroy (&bptr);
600 nis_free_directory (dir);
602 __set_errno (saved_errno);
604 return retcode;