Update.
[glibc.git] / nis / nis_call.c
blobaaefdd22ba7683e2162eae235dded3d4a8925c76
1 /* Copyright (C) 1997 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 <fcntl.h>
21 #include <string.h>
22 #include <rpc/rpc.h>
23 #include <rpc/auth.h>
24 #include <rpcsvc/nis.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28 #include "nis_intern.h"
30 static struct timeval TIMEOUT = {10, 0};
32 unsigned long
33 inetstr2int (const char *str)
35 char buffer[strlen (str) + 3];
36 size_t buflen;
37 size_t i, j;
39 buflen = stpcpy (buffer, str) - buffer;
41 j = 0;
42 for (i = 0; i < buflen; ++i)
43 if (buffer[i] == '.')
45 ++j;
46 if (j == 4)
48 buffer[i] = '\0';
49 break;
53 return inet_addr (buffer);
56 static void
57 __bind_destroy (dir_binding *bind)
59 if (bind->clnt != NULL)
61 if (bind->use_auth)
62 auth_destroy (bind->clnt->cl_auth);
63 clnt_destroy (bind->clnt);
65 free (bind->server_val);
66 free (bind);
69 static nis_error
70 __bind_next (dir_binding *bind)
72 u_int j;
74 if (bind->clnt != NULL)
76 if (bind->use_auth)
77 auth_destroy (bind->clnt->cl_auth);
78 clnt_destroy (bind->clnt);
79 bind->clnt = NULL;
82 if (bind->trys >= bind->server_len)
83 return NIS_FAIL;
85 for (j = bind->current_ep + 1;
86 j < bind->server_val[bind->server_used].ep.ep_len; ++j)
87 if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
88 "inet") == 0)
89 if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].proto,
90 "-") == 0)
92 bind->current_ep = j;
93 return NIS_SUCCESS;
96 ++bind->trys;
97 ++bind->server_used;
98 if (bind->server_used >= bind->server_len)
99 bind->server_used = 0;
101 for (j = 0; j < bind->server_val[bind->server_used].ep.ep_len; ++j)
102 if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
103 "inet") == 0)
104 if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].proto,
105 "-") == 0)
107 bind->current_ep = j;
108 return NIS_SUCCESS;
111 return NIS_FAIL;
114 static nis_error
115 __bind_connect (dir_binding *dbp)
117 struct sockaddr_in check;
118 nis_server *serv;
119 int checklen;
121 if (dbp == NULL)
122 return NIS_FAIL;
124 serv = &dbp->server_val[dbp->server_used];
126 memset (&dbp->addr, '\0', sizeof (dbp->addr));
127 dbp->addr.sin_family = AF_INET;
129 dbp->addr.sin_addr.s_addr =
130 inetstr2int (serv->ep.ep_val[dbp->current_ep].uaddr);
132 if (dbp->addr.sin_addr.s_addr == 0)
133 return NIS_FAIL;
135 dbp->socket = RPC_ANYSOCK;
136 if (dbp->use_udp)
137 dbp->clnt = clntudp_create (&dbp->addr, NIS_PROG, NIS_VERSION,
138 TIMEOUT, &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)&TIMEOUT);
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 p = strchr (netname, '.');
162 *p = '@';
163 dbp->clnt->cl_auth =
164 authdes_pk_create (netname, &serv->pkey, 300, NULL, NULL);
165 if (!dbp->clnt->cl_auth)
166 dbp->clnt->cl_auth = authunix_create_default ();
168 else
169 dbp->clnt->cl_auth = authunix_create_default ();
170 dbp->use_auth = TRUE;
173 /* Get port for sanity checks later */
174 checklen = sizeof (struct sockaddr_in);
175 memset (&check, 0, checklen);
176 if (dbp->use_udp)
177 bind (dbp->socket, (struct sockaddr *)&check, checklen);
178 check.sin_family = AF_INET;
179 if (!getsockname (dbp->socket, (struct sockaddr *)&check, &checklen))
180 dbp->port = check.sin_port;
182 dbp->create = time (NULL);
184 return NIS_SUCCESS;
187 static dir_binding *
188 __bind_create (const nis_server *serv_val, u_int serv_len, u_long flags)
190 dir_binding *dbp;
191 u_int i;
193 dbp = calloc (1, sizeof (dir_binding));
194 if (dbp == NULL)
195 return NULL;
197 dbp->server_len = serv_len;
198 dbp->server_val = calloc (1, sizeof (nis_server) * serv_len);
199 if (dbp->server_val == NULL)
201 free (dbp);
202 return NULL;
205 if (flags & USE_DGRAM)
206 dbp->use_udp = TRUE;
207 else
208 dbp->use_udp = FALSE;
210 if (flags & NO_AUTHINFO)
211 dbp->use_auth = FALSE;
212 else
213 dbp->use_auth = TRUE;
215 if (flags & MASTER_ONLY)
216 dbp->master_only = TRUE;
217 else
218 dbp->master_only = FALSE;
220 dbp->trys = 1;
222 for (i = 0; i < serv_len; ++i)
224 if (serv_val[i].name != NULL)
225 dbp->server_val[i].name = strdup (serv_val[i].name);
227 dbp->server_val[i].ep.ep_len = serv_val[i].ep.ep_len;
228 if (dbp->server_val[i].ep.ep_len > 0)
230 unsigned long j;
232 dbp->server_val[i].ep.ep_val =
233 malloc (serv_val[i].ep.ep_len * sizeof (endpoint));
234 for (j = 0; j < dbp->server_val[i].ep.ep_len; ++j)
236 if (serv_val[i].ep.ep_val[j].uaddr)
237 dbp->server_val[i].ep.ep_val[j].uaddr =
238 strdup (serv_val[i].ep.ep_val[j].uaddr);
239 else
240 dbp->server_val[i].ep.ep_val[j].uaddr = NULL;
241 if (serv_val[i].ep.ep_val[j].family)
242 dbp->server_val[i].ep.ep_val[j].family =
243 strdup (serv_val[i].ep.ep_val[j].family);
244 else
245 dbp->server_val[i].ep.ep_val[j].family = NULL;
246 if (serv_val[i].ep.ep_val[j].proto)
247 dbp->server_val[i].ep.ep_val[j].proto =
248 strdup (serv_val[i].ep.ep_val[j].proto);
249 else
250 dbp->server_val[i].ep.ep_val[j].proto = NULL;
253 else
254 dbp->server_val[i].ep.ep_val = NULL;
255 dbp->server_val[i].key_type = serv_val[i].key_type;
256 dbp->server_val[i].pkey.n_len = serv_val[i].pkey.n_len;
257 if (serv_val[i].pkey.n_len > 0)
259 dbp->server_val[i].pkey.n_bytes =
260 malloc (serv_val[i].pkey.n_len);
261 if (dbp->server_val[i].pkey.n_bytes == NULL)
262 return NULL;
263 memcpy (dbp->server_val[i].pkey.n_bytes, serv_val[i].pkey.n_bytes,
264 serv_val[i].pkey.n_len);
266 else
267 dbp->server_val[i].pkey.n_bytes = NULL;
270 if (__nis_findfastest (dbp) < 1)
272 __bind_destroy (dbp);
273 return NULL;
276 return dbp;
279 nis_error
280 __do_niscall2 (const nis_server *server, u_int server_len, u_long prog,
281 xdrproc_t xargs, caddr_t req, xdrproc_t xres, caddr_t resp,
282 u_long flags, nis_cb *cb)
284 enum clnt_stat result;
285 nis_error retcode;
286 dir_binding *dbp;
288 if (flags & MASTER_ONLY)
289 server_len = 1;
291 if ((dbp = __bind_create (server, server_len, flags)) == NULL)
292 return NIS_NAMEUNREACHABLE;
293 while (__bind_connect (dbp) != NIS_SUCCESS)
295 if (__bind_next (dbp) != NIS_SUCCESS)
297 __bind_destroy (dbp);
298 return NIS_NAMEUNREACHABLE;
304 again:
305 result = clnt_call (dbp->clnt, prog, xargs, req, xres, resp, TIMEOUT);
307 if (result != RPC_SUCCESS)
309 clnt_perror (dbp->clnt, "__do_niscall2: clnt_call");
310 __bind_destroy (dbp);
311 retcode = NIS_RPCERROR;
313 else
315 switch (prog)
317 case NIS_IBLIST:
318 if ((((nis_result *)resp)->status == NIS_CBRESULTS) &&
319 (cb != NULL))
321 __nis_do_callback(dbp, &((nis_result *)resp)->cookie, cb);
322 break;
324 /* Yes, this is correct. If we doesn't have to start
325 a callback, look if we have to search another server */
326 case NIS_LOOKUP:
327 case NIS_ADD:
328 case NIS_MODIFY:
329 case NIS_REMOVE:
330 case NIS_IBADD:
331 case NIS_IBMODIFY:
332 case NIS_IBREMOVE:
333 case NIS_IBFIRST:
334 case NIS_IBNEXT:
335 if ((((nis_result *)resp)->status == NIS_NOTFOUND) ||
336 (((nis_result *)resp)->status == NIS_NOSUCHNAME) ||
337 (((nis_result *)resp)->status == NIS_NOT_ME))
339 if (__bind_next (dbp) == NIS_SUCCESS)
341 while (__bind_connect (dbp) != NIS_SUCCESS)
343 if (__bind_next (dbp) != NIS_SUCCESS)
345 __bind_destroy (dbp);
346 return NIS_SUCCESS;
350 else
351 break; /* No more servers to search in */
352 goto again;
354 break;
355 case NIS_FINDDIRECTORY:
356 if ((((fd_result *)resp)->status == NIS_NOTFOUND) ||
357 (((fd_result *)resp)->status == NIS_NOSUCHNAME) ||
358 (((fd_result *)resp)->status == NIS_NOT_ME))
360 if (__bind_next (dbp) == NIS_SUCCESS)
362 while (__bind_connect (dbp) != NIS_SUCCESS)
364 if (__bind_next (dbp) != NIS_SUCCESS)
366 __bind_destroy (dbp);
367 return NIS_SUCCESS;
371 else
372 break; /* No more servers to search in */
373 goto again;
375 break;
376 case NIS_DUMPLOG: /* log_result */
377 case NIS_DUMP:
378 if ((((log_result *)resp)->lr_status == NIS_NOTFOUND) ||
379 (((log_result *)resp)->lr_status == NIS_NOSUCHNAME) ||
380 (((log_result *)resp)->lr_status == NIS_NOT_ME))
382 if (__bind_next (dbp) == NIS_SUCCESS)
384 while (__bind_connect (dbp) != NIS_SUCCESS)
386 if (__bind_next (dbp) != NIS_SUCCESS)
388 __bind_destroy (dbp);
389 return NIS_SUCCESS;
393 else
394 break; /* No more servers to search in */
395 goto again;
397 break;
398 default:
399 break;
401 __bind_destroy (dbp);
402 retcode = NIS_SUCCESS;
405 while ((flags & HARD_LOOKUP) && retcode == NIS_RPCERROR);
407 return retcode;
410 static directory_obj *
411 rec_dirsearch (const_nis_name name, directory_obj *dir, u_long flags,
412 nis_error *status)
414 fd_result *fd_res;
415 XDR xdrs;
416 char domain [strlen (name) + 3];
418 nis_domain_of_r (name, domain, sizeof (domain));
419 if (strncmp (domain, "org_dir.", 8) == 0)
421 char tmp[strlen (name) + 3];
423 nis_domain_of_r (domain, tmp, sizeof (tmp));
424 strcpy (domain, tmp);
426 else
427 if (strncmp (domain, "groups_dir.", 11) == 0)
429 char tmp[strlen (name) + 3];
431 nis_domain_of_r (domain, tmp, sizeof (tmp));
432 strcpy (domain, tmp);
434 else
436 /* We have no grous_dir or org_dir, so try the complete name */
437 strcpy (domain, name);
440 switch (nis_dir_cmp (domain, dir->do_name))
442 case SAME_NAME:
443 *status = NIS_SUCCESS;
444 return dir;
445 case NOT_SEQUENTIAL:
446 /* NOT_SEQUENTIAL means, go one up and try it there ! */
447 case HIGHER_NAME:
448 { /* We need data from a parent domain */
449 directory_obj *obj;
450 char ndomain [strlen (name) + 3];
452 nis_domain_of_r (dir->do_name, ndomain, sizeof (ndomain));
454 /* The root server of our domain is a replica of the parent
455 domain ! (Now I understand why a root server must be a
456 replica of the parent domain) */
457 fd_res = __nis_finddirectory (dir, ndomain);
458 *status = fd_res->status;
459 if (fd_res->status != NIS_SUCCESS)
461 nis_free_directory (dir);
462 xdr_free((xdrproc_t)xdr_fd_result, (caddr_t)fd_res);
463 return NULL;
465 obj = calloc(1, sizeof(directory_obj));
466 xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
467 fd_res->dir_data.dir_data_len, XDR_DECODE);
468 xdr_directory_obj(&xdrs, obj);
469 xdr_destroy(&xdrs);
470 __free_fdresult (fd_res);
471 if (obj != NULL)
473 /* We have found a NIS+ server serving ndomain, now
474 let us search for "name" */
475 nis_free_directory (dir);
476 return rec_dirsearch (name, obj, flags, status);
478 else
480 /* Ups, very bad. Are we already the root server ? */
481 nis_free_directory (dir);
482 return NULL;
485 break;
486 case LOWER_NAME:
488 directory_obj *obj;
489 char leaf [strlen (name) + 3];
490 char ndomain [strlen (name) + 3];
491 char *cp;
495 if (strlen (domain) == 0)
497 nis_free_directory (dir);
498 return NULL;
500 nis_leaf_of_r (domain, leaf, sizeof (leaf));
501 nis_domain_of_r (domain, ndomain, sizeof (ndomain));
502 strcpy (domain, ndomain);
504 while (nis_dir_cmp (domain, dir->do_name) != SAME_NAME);
505 cp = strchr (leaf, '\0');
506 *cp++ = '.';
507 strcpy (cp, domain);
509 fd_res = __nis_finddirectory (dir, leaf);
510 *status = fd_res->status;
511 if (fd_res->status != NIS_SUCCESS)
513 nis_free_directory (dir);
514 xdr_free((xdrproc_t)xdr_fd_result, (caddr_t)fd_res);
515 return NULL;
517 obj = calloc(1, sizeof(directory_obj));
518 xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
519 fd_res->dir_data.dir_data_len, XDR_DECODE);
520 xdr_directory_obj(&xdrs, obj);
521 xdr_destroy(&xdrs);
522 __free_fdresult (fd_res);
523 if (obj != NULL)
525 /* We have found a NIS+ server serving ndomain, now
526 let us search for "name" */
527 nis_free_directory (dir);
528 return rec_dirsearch (name, obj, flags, status);
531 break;
532 case BAD_NAME:
533 nis_free_directory (dir);
534 *status = NIS_BADNAME;
535 return NULL;
537 nis_free_directory (dir);
538 *status = NIS_FAIL;
539 return NULL;
542 nis_error
543 __do_niscall (const_nis_name name, u_long prog, xdrproc_t xargs,
544 caddr_t req, xdrproc_t xres, caddr_t resp, u_long flags,
545 nis_cb *cb)
547 nis_error retcode;
548 directory_obj *dir = NULL;
549 nis_server *server;
550 u_int server_len;
552 if (name == NULL)
553 return NIS_BADNAME;
555 if (dir == NULL)
557 nis_error status;
558 dir = readColdStartFile ();
559 if (dir == NULL) /* No /var/nis/NIS_COLD_START->no NIS+ installed */
560 return NIS_UNAVAIL;
562 dir = rec_dirsearch (name, dir, flags, &status);
563 if (dir == NULL)
564 return status;
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;
579 retcode = __do_niscall2 (server, server_len, prog, xargs, req, xres, resp,
580 flags, cb);
582 nis_free_directory (dir);
584 return retcode;