Update.
[glibc.git] / nis / nis_call.c
blobaffae099cf6f44084c1480d227896fd99baf0078
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>
29 #include "nis_intern.h"
31 static struct timeval RPCTIMEOUT = {10, 0};
32 static struct timeval UDPTIMEOUT = {5, 0};
34 unsigned long
35 inetstr2int (const char *str)
37 char buffer[strlen (str) + 3];
38 size_t buflen;
39 size_t i, j;
41 buflen = stpcpy (buffer, str) - buffer;
43 j = 0;
44 for (i = 0; i < buflen; ++i)
45 if (buffer[i] == '.')
47 ++j;
48 if (j == 4)
50 buffer[i] = '\0';
51 break;
55 return inet_addr (buffer);
58 static void
59 __bind_destroy (dir_binding *bind)
61 if (bind->clnt != NULL)
63 if (bind->use_auth)
64 auth_destroy (bind->clnt->cl_auth);
65 clnt_destroy (bind->clnt);
67 free (bind->server_val);
68 free (bind);
71 static nis_error
72 __bind_next (dir_binding *bind)
74 u_int j;
76 if (bind->clnt != NULL)
78 if (bind->use_auth)
79 auth_destroy (bind->clnt->cl_auth);
80 clnt_destroy (bind->clnt);
81 bind->clnt = NULL;
84 if (bind->trys >= bind->server_len)
85 return NIS_FAIL;
87 for (j = bind->current_ep + 1;
88 j < bind->server_val[bind->server_used].ep.ep_len; ++j)
89 if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
90 "inet") == 0)
91 if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].proto,
92 "-") == 0)
94 bind->current_ep = j;
95 return NIS_SUCCESS;
98 ++bind->trys;
99 ++bind->server_used;
100 if (bind->server_used >= bind->server_len)
101 bind->server_used = 0;
103 for (j = 0; j < bind->server_val[bind->server_used].ep.ep_len; ++j)
104 if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
105 "inet") == 0)
106 if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].proto,
107 "-") == 0)
109 bind->current_ep = j;
110 return NIS_SUCCESS;
113 return NIS_FAIL;
116 static nis_error
117 __bind_connect (dir_binding *dbp)
119 struct sockaddr_in check;
120 nis_server *serv;
121 int checklen;
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 dbp->socket = RPC_ANYSOCK;
138 if (dbp->use_udp)
139 dbp->clnt = clntudp_create (&dbp->addr, NIS_PROG, NIS_VERSION,
140 UDPTIMEOUT, &dbp->socket);
141 else
142 dbp->clnt = clnttcp_create (&dbp->addr, NIS_PROG, NIS_VERSION,
143 &dbp->socket, 0, 0);
145 if (dbp->clnt == NULL)
146 return NIS_RPCERROR;
148 clnt_control (dbp->clnt, CLSET_TIMEOUT, (caddr_t)&RPCTIMEOUT);
149 /* If the program exists, close the socket */
150 if (fcntl (dbp->socket, F_SETFD, 1) == -1)
151 perror (_("fcntl: F_SETFD"));
153 if (dbp->use_auth)
155 if (serv->key_type == NIS_PK_DH)
157 char netname[MAXNETNAMELEN+1];
158 char *p;
160 p = stpcpy (netname, "unix.");
161 strncpy (p, serv->name,MAXNETNAMELEN-5);
162 netname[MAXNETNAMELEN] = '\0';
163 p = strchr (netname, '.');
164 *p = '@';
165 dbp->clnt->cl_auth =
166 authdes_pk_create (netname, &serv->pkey, 300, NULL, NULL);
167 if (!dbp->clnt->cl_auth)
168 dbp->clnt->cl_auth = authunix_create_default ();
170 else
171 dbp->clnt->cl_auth = authunix_create_default ();
172 dbp->use_auth = TRUE;
175 /* Get port for sanity checks later */
176 checklen = sizeof (struct sockaddr_in);
177 memset (&check, 0, checklen);
178 if (dbp->use_udp)
179 bind (dbp->socket, (struct sockaddr *)&check, checklen);
180 check.sin_family = AF_INET;
181 if (!getsockname (dbp->socket, (struct sockaddr *)&check, &checklen))
182 dbp->port = check.sin_port;
184 dbp->create = time (NULL);
186 return NIS_SUCCESS;
189 static dir_binding *
190 __bind_create (const nis_server *serv_val, u_int serv_len, u_long flags,
191 cache2_info *cinfo)
193 dir_binding *dbp;
194 u_int i;
196 dbp = calloc (1, sizeof (dir_binding));
197 if (dbp == NULL)
198 return NULL;
200 dbp->server_len = serv_len;
201 dbp->server_val = calloc (1, sizeof (nis_server) * serv_len);
202 if (dbp->server_val == NULL)
204 free (dbp);
205 return NULL;
208 if (flags & USE_DGRAM)
209 dbp->use_udp = TRUE;
210 else
211 dbp->use_udp = FALSE;
213 if (flags & NO_AUTHINFO)
214 dbp->use_auth = FALSE;
215 else
216 dbp->use_auth = TRUE;
218 if (flags & MASTER_ONLY)
219 dbp->master_only = TRUE;
220 else
221 dbp->master_only = FALSE;
223 dbp->trys = 1;
225 for (i = 0; i < serv_len; ++i)
227 if (serv_val[i].name != NULL)
228 dbp->server_val[i].name = strdup (serv_val[i].name);
230 dbp->server_val[i].ep.ep_len = serv_val[i].ep.ep_len;
231 if (dbp->server_val[i].ep.ep_len > 0)
233 unsigned long j;
235 dbp->server_val[i].ep.ep_val =
236 malloc (serv_val[i].ep.ep_len * sizeof (endpoint));
237 for (j = 0; j < dbp->server_val[i].ep.ep_len; ++j)
239 if (serv_val[i].ep.ep_val[j].uaddr)
240 dbp->server_val[i].ep.ep_val[j].uaddr =
241 strdup (serv_val[i].ep.ep_val[j].uaddr);
242 else
243 dbp->server_val[i].ep.ep_val[j].uaddr = NULL;
244 if (serv_val[i].ep.ep_val[j].family)
245 dbp->server_val[i].ep.ep_val[j].family =
246 strdup (serv_val[i].ep.ep_val[j].family);
247 else
248 dbp->server_val[i].ep.ep_val[j].family = NULL;
249 if (serv_val[i].ep.ep_val[j].proto)
250 dbp->server_val[i].ep.ep_val[j].proto =
251 strdup (serv_val[i].ep.ep_val[j].proto);
252 else
253 dbp->server_val[i].ep.ep_val[j].proto = NULL;
256 else
257 dbp->server_val[i].ep.ep_val = NULL;
258 dbp->server_val[i].key_type = serv_val[i].key_type;
259 dbp->server_val[i].pkey.n_len = serv_val[i].pkey.n_len;
260 if (serv_val[i].pkey.n_len > 0)
262 dbp->server_val[i].pkey.n_bytes =
263 malloc (serv_val[i].pkey.n_len);
264 if (dbp->server_val[i].pkey.n_bytes == NULL)
265 return NULL;
266 memcpy (dbp->server_val[i].pkey.n_bytes, serv_val[i].pkey.n_bytes,
267 serv_val[i].pkey.n_len);
269 else
270 dbp->server_val[i].pkey.n_bytes = NULL;
273 dbp->class = -1;
274 if (cinfo != NULL && cinfo->server_used >= 0)
276 dbp->server_used = cinfo->server_used;
277 dbp->current_ep = cinfo->current_ep;
278 dbp->class = cinfo->class;
280 else if (__nis_findfastest (dbp) < 1)
282 __bind_destroy (dbp);
283 return NULL;
286 return dbp;
289 nis_error
290 __do_niscall2 (const nis_server *server, u_int server_len, u_long prog,
291 xdrproc_t xargs, caddr_t req, xdrproc_t xres, caddr_t resp,
292 u_long flags, nis_cb *cb, cache2_info *cinfo)
294 enum clnt_stat result;
295 nis_error retcode;
296 dir_binding *dbp;
298 if (flags & MASTER_ONLY)
299 server_len = 1;
301 dbp = __bind_create (server, server_len, flags, cinfo);
302 if (dbp == NULL)
303 return NIS_NAMEUNREACHABLE;
304 while (__bind_connect (dbp) != NIS_SUCCESS)
306 if (__bind_next (dbp) != NIS_SUCCESS)
308 __bind_destroy (dbp);
309 return NIS_NAMEUNREACHABLE;
315 again:
316 result = clnt_call (dbp->clnt, prog, xargs, req, xres, resp, RPCTIMEOUT);
318 if (result != RPC_SUCCESS)
320 clnt_perror (dbp->clnt, "__do_niscall2: clnt_call");
321 __bind_destroy (dbp);
322 retcode = NIS_RPCERROR;
324 else
326 switch (prog)
328 case NIS_IBLIST:
329 if ((((nis_result *)resp)->status == NIS_CBRESULTS) &&
330 (cb != NULL))
332 __nis_do_callback(dbp, &((nis_result *)resp)->cookie, cb);
333 break;
335 /* Yes, this is correct. If we doesn't have to start
336 a callback, look if we have to search another server */
337 case NIS_LOOKUP:
338 case NIS_ADD:
339 case NIS_MODIFY:
340 case NIS_REMOVE:
341 case NIS_IBADD:
342 case NIS_IBMODIFY:
343 case NIS_IBREMOVE:
344 case NIS_IBFIRST:
345 case NIS_IBNEXT:
346 if ((((nis_result *)resp)->status == NIS_NOTFOUND) ||
347 (((nis_result *)resp)->status == NIS_NOSUCHNAME) ||
348 (((nis_result *)resp)->status == NIS_NOT_ME))
350 if (__bind_next (dbp) == NIS_SUCCESS)
352 while (__bind_connect (dbp) != NIS_SUCCESS)
354 if (__bind_next (dbp) != NIS_SUCCESS)
356 __bind_destroy (dbp);
357 return NIS_SUCCESS;
361 else
362 break; /* No more servers to search in */
363 goto again;
365 break;
366 case NIS_FINDDIRECTORY:
367 if ((((fd_result *)resp)->status == NIS_NOTFOUND) ||
368 (((fd_result *)resp)->status == NIS_NOSUCHNAME) ||
369 (((fd_result *)resp)->status == NIS_NOT_ME))
371 if (__bind_next (dbp) == NIS_SUCCESS)
373 while (__bind_connect (dbp) != NIS_SUCCESS)
375 if (__bind_next (dbp) != NIS_SUCCESS)
377 __bind_destroy (dbp);
378 return NIS_SUCCESS;
382 else
383 break; /* No more servers to search in */
384 goto again;
386 break;
387 case NIS_DUMPLOG: /* log_result */
388 case NIS_DUMP:
389 if ((((log_result *)resp)->lr_status == NIS_NOTFOUND) ||
390 (((log_result *)resp)->lr_status == NIS_NOSUCHNAME) ||
391 (((log_result *)resp)->lr_status == NIS_NOT_ME))
393 if (__bind_next (dbp) == NIS_SUCCESS)
395 while (__bind_connect (dbp) != NIS_SUCCESS)
397 if (__bind_next (dbp) != NIS_SUCCESS)
399 __bind_destroy (dbp);
400 return NIS_SUCCESS;
404 else
405 break; /* No more servers to search in */
406 goto again;
408 break;
409 default:
410 break;
412 __bind_destroy (dbp);
413 retcode = NIS_SUCCESS;
416 while ((flags & HARD_LOOKUP) && retcode == NIS_RPCERROR);
418 return retcode;
421 static directory_obj *
422 rec_dirsearch (const_nis_name name, directory_obj *dir, u_long flags,
423 nis_error *status)
425 fd_result *fd_res;
426 XDR xdrs;
427 char domain [strlen (name) + 3];
429 nis_domain_of_r (name, domain, sizeof (domain));
430 if (strncmp (domain, "org_dir.", 8) == 0)
432 char tmp[strlen (name) + 3];
434 nis_domain_of_r (domain, tmp, sizeof (tmp));
435 strcpy (domain, tmp);
437 else
438 if (strncmp (domain, "groups_dir.", 11) == 0)
440 char tmp[strlen (name) + 3];
442 nis_domain_of_r (domain, tmp, sizeof (tmp));
443 strcpy (domain, tmp);
445 else
447 /* We have no grous_dir or org_dir, so try the complete name */
448 strcpy (domain, name);
451 switch (nis_dir_cmp (domain, dir->do_name))
453 case SAME_NAME:
454 *status = NIS_SUCCESS;
455 return dir;
456 case NOT_SEQUENTIAL:
457 /* NOT_SEQUENTIAL means, go one up and try it there ! */
458 case HIGHER_NAME:
459 { /* We need data from a parent domain */
460 directory_obj *obj;
461 char ndomain [strlen (name) + 3];
463 nis_domain_of_r (dir->do_name, ndomain, sizeof (ndomain));
465 /* The root server of our domain is a replica of the parent
466 domain ! (Now I understand why a root server must be a
467 replica of the parent domain) */
468 fd_res = __nis_finddirectory (dir, ndomain);
469 *status = fd_res->status;
470 if (fd_res->status != NIS_SUCCESS)
472 nis_free_directory (dir);
473 __free_fdresult (fd_res);
474 return NULL;
476 obj = calloc(1, sizeof(directory_obj));
477 xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
478 fd_res->dir_data.dir_data_len, XDR_DECODE);
479 xdr_directory_obj(&xdrs, obj);
480 xdr_destroy(&xdrs);
481 __free_fdresult (fd_res);
482 if (obj != NULL)
484 /* We have found a NIS+ server serving ndomain, now
485 let us search for "name" */
486 nis_free_directory (dir);
487 return rec_dirsearch (name, obj, flags, status);
489 else
491 /* Ups, very bad. Are we already the root server ? */
492 nis_free_directory (dir);
493 return NULL;
496 break;
497 case LOWER_NAME:
499 directory_obj *obj;
500 char leaf [strlen (name) + 3];
501 char ndomain [strlen (name) + 3];
502 char *cp;
506 if (strlen (domain) == 0)
508 nis_free_directory (dir);
509 return NULL;
511 nis_leaf_of_r (domain, leaf, sizeof (leaf));
512 nis_domain_of_r (domain, ndomain, sizeof (ndomain));
513 strcpy (domain, ndomain);
515 while (nis_dir_cmp (domain, dir->do_name) != SAME_NAME);
516 cp = strchr (leaf, '\0');
517 *cp++ = '.';
518 strcpy (cp, domain);
520 fd_res = __nis_finddirectory (dir, leaf);
521 *status = fd_res->status;
522 if (fd_res->status != NIS_SUCCESS)
524 nis_free_directory (dir);
525 __free_fdresult (fd_res);
526 return NULL;
528 obj = calloc(1, sizeof(directory_obj));
529 xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
530 fd_res->dir_data.dir_data_len, XDR_DECODE);
531 xdr_directory_obj(&xdrs, obj);
532 xdr_destroy(&xdrs);
533 __free_fdresult (fd_res);
534 if (obj != NULL)
536 /* We have found a NIS+ server serving ndomain, now
537 let us search for "name" */
538 nis_free_directory (dir);
539 return rec_dirsearch (name, obj, flags, status);
542 break;
543 case BAD_NAME:
544 nis_free_directory (dir);
545 *status = NIS_BADNAME;
546 return NULL;
548 nis_free_directory (dir);
549 *status = NIS_FAIL;
550 return NULL;
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, u_long flags,
556 nis_cb *cb)
558 nis_error retcode;
559 directory_obj *dir = NULL;
560 nis_server *server;
561 u_int server_len;
562 cache2_info cinfo = {-1, -1, -1};
563 int saved_errno = errno;
565 if (name == NULL)
566 return NIS_BADNAME;
568 /* Search in local cache. In the moment, we ignore the fastest server */
569 if (!(flags & NO_CACHE))
570 dir = __nis_cache_search (name, flags, &cinfo);
572 if (dir == NULL)
574 nis_error status;
575 dir = readColdStartFile ();
576 if (dir == NULL) /* No /var/nis/NIS_COLD_START->no NIS+ installed */
578 __set_errno (saved_errno);
579 return NIS_UNAVAIL;
582 dir = rec_dirsearch (name, dir, flags, &status);
583 if (dir == NULL)
585 __set_errno (saved_errno);
586 return status;
590 if (flags & MASTER_ONLY)
592 server = dir->do_servers.do_servers_val;
593 server_len = 1;
595 else
597 server = dir->do_servers.do_servers_val;
598 server_len = dir->do_servers.do_servers_len;
602 retcode = __do_niscall2 (server, server_len, prog, xargs, req, xres, resp,
603 flags, cb, &cinfo);
605 nis_free_directory (dir);
607 __set_errno (saved_errno);
609 return retcode;