]> git.ipfire.org Git - thirdparty/glibc.git/blob - nis/nis_call.c
Update.
[thirdparty/glibc.git] / nis / nis_call.c
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.
4
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.
9
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.
14
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. */
19
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"
30
31 static struct timeval RPCTIMEOUT = {10, 0};
32 static struct timeval UDPTIMEOUT = {5, 0};
33
34 extern u_short __pmap_getnisport (struct sockaddr_in *address, u_long program,
35 u_long version, u_int protocol);
36
37 unsigned long
38 inetstr2int (const char *str)
39 {
40 char buffer[strlen (str) + 3];
41 size_t buflen;
42 size_t i, j;
43
44 buflen = stpcpy (buffer, str) - buffer;
45
46 j = 0;
47 for (i = 0; i < buflen; ++i)
48 if (buffer[i] == '.')
49 {
50 ++j;
51 if (j == 4)
52 {
53 buffer[i] = '\0';
54 break;
55 }
56 }
57
58 return inet_addr (buffer);
59 }
60
61 static void
62 __bind_destroy (dir_binding *bind)
63 {
64 if (bind->clnt != NULL)
65 {
66 if (bind->use_auth)
67 auth_destroy (bind->clnt->cl_auth);
68 clnt_destroy (bind->clnt);
69 }
70 free (bind->server_val);
71 free (bind);
72 }
73
74 static nis_error
75 __bind_next (dir_binding *bind)
76 {
77 u_int j;
78
79 if (bind->clnt != NULL)
80 {
81 if (bind->use_auth)
82 auth_destroy (bind->clnt->cl_auth);
83 clnt_destroy (bind->clnt);
84 bind->clnt = NULL;
85 }
86
87 if (bind->trys >= bind->server_len)
88 return NIS_FAIL;
89
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)
96 {
97 bind->current_ep = j;
98 return NIS_SUCCESS;
99 }
100
101 ++bind->trys;
102 ++bind->server_used;
103 if (bind->server_used >= bind->server_len)
104 bind->server_used = 0;
105
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 (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].proto,
110 "-") == 0)
111 {
112 bind->current_ep = j;
113 return NIS_SUCCESS;
114 }
115
116 return NIS_FAIL;
117 }
118
119 static nis_error
120 __bind_connect (dir_binding *dbp)
121 {
122 struct sockaddr_in check;
123 nis_server *serv;
124 int checklen;
125
126 if (dbp == NULL)
127 return NIS_FAIL;
128
129 serv = &dbp->server_val[dbp->server_used];
130
131 memset (&dbp->addr, '\0', sizeof (dbp->addr));
132 dbp->addr.sin_family = AF_INET;
133
134 dbp->addr.sin_addr.s_addr =
135 inetstr2int (serv->ep.ep_val[dbp->current_ep].uaddr);
136
137 if (dbp->addr.sin_addr.s_addr == 0)
138 return NIS_FAIL;
139
140 /* Check, if the host is online and rpc.nisd is running. Much faster
141 then the clnt*_create functions: */
142 if (__pmap_getnisport (&dbp->addr, NIS_PROG, NIS_VERSION, IPPROTO_UDP) == 0)
143 return NIS_RPCERROR;
144
145 dbp->socket = RPC_ANYSOCK;
146 if (dbp->use_udp)
147 dbp->clnt = clntudp_create (&dbp->addr, NIS_PROG, NIS_VERSION,
148 UDPTIMEOUT, &dbp->socket);
149 else
150 dbp->clnt = clnttcp_create (&dbp->addr, NIS_PROG, NIS_VERSION,
151 &dbp->socket, 0, 0);
152
153 if (dbp->clnt == NULL)
154 return NIS_RPCERROR;
155
156 clnt_control (dbp->clnt, CLSET_TIMEOUT, (caddr_t)&RPCTIMEOUT);
157 /* If the program exists, close the socket */
158 if (fcntl (dbp->socket, F_SETFD, 1) == -1)
159 perror (_("fcntl: F_SETFD"));
160
161 if (dbp->use_auth)
162 {
163 if (serv->key_type == NIS_PK_DH && key_secretkey_is_set ())
164 {
165 char netname[MAXNETNAMELEN+1];
166 char *p;
167
168 p = stpcpy (netname, "unix.");
169 strncpy (p, serv->name,MAXNETNAMELEN-5);
170 netname[MAXNETNAMELEN] = '\0';
171 p = strchr (netname, '.');
172 *p = '@';
173 dbp->clnt->cl_auth =
174 authdes_pk_create (netname, &serv->pkey, 300, NULL, NULL);
175 if (!dbp->clnt->cl_auth)
176 dbp->clnt->cl_auth = authunix_create_default ();
177 }
178 else
179 dbp->clnt->cl_auth = authunix_create_default ();
180 dbp->use_auth = TRUE;
181 }
182
183 /* Get port for sanity checks later */
184 checklen = sizeof (struct sockaddr_in);
185 memset (&check, 0, checklen);
186 if (dbp->use_udp)
187 bind (dbp->socket, (struct sockaddr *)&check, checklen);
188 check.sin_family = AF_INET;
189 if (!getsockname (dbp->socket, (struct sockaddr *)&check, &checklen))
190 dbp->port = check.sin_port;
191
192 dbp->create = time (NULL);
193
194 return NIS_SUCCESS;
195 }
196
197 static dir_binding *
198 __bind_create (const nis_server *serv_val, u_int serv_len, u_long flags,
199 cache2_info *cinfo)
200 {
201 dir_binding *dbp;
202 u_int i;
203
204 dbp = calloc (1, sizeof (dir_binding));
205 if (dbp == NULL)
206 return NULL;
207
208 dbp->server_len = serv_len;
209 dbp->server_val = calloc (1, sizeof (nis_server) * serv_len);
210 if (dbp->server_val == NULL)
211 {
212 free (dbp);
213 return NULL;
214 }
215
216 if (flags & USE_DGRAM)
217 dbp->use_udp = TRUE;
218 else
219 dbp->use_udp = FALSE;
220
221 if (flags & NO_AUTHINFO)
222 dbp->use_auth = FALSE;
223 else
224 dbp->use_auth = TRUE;
225
226 if (flags & MASTER_ONLY)
227 dbp->master_only = TRUE;
228 else
229 dbp->master_only = FALSE;
230
231 /* We try the first server */
232 dbp->trys = 1;
233
234 for (i = 0; i < serv_len; ++i)
235 {
236 if (serv_val[i].name != NULL)
237 dbp->server_val[i].name = strdup (serv_val[i].name);
238
239 dbp->server_val[i].ep.ep_len = serv_val[i].ep.ep_len;
240 if (dbp->server_val[i].ep.ep_len > 0)
241 {
242 unsigned long j;
243
244 dbp->server_val[i].ep.ep_val =
245 malloc (serv_val[i].ep.ep_len * sizeof (endpoint));
246 for (j = 0; j < dbp->server_val[i].ep.ep_len; ++j)
247 {
248 if (serv_val[i].ep.ep_val[j].uaddr)
249 dbp->server_val[i].ep.ep_val[j].uaddr =
250 strdup (serv_val[i].ep.ep_val[j].uaddr);
251 else
252 dbp->server_val[i].ep.ep_val[j].uaddr = NULL;
253 if (serv_val[i].ep.ep_val[j].family)
254 dbp->server_val[i].ep.ep_val[j].family =
255 strdup (serv_val[i].ep.ep_val[j].family);
256 else
257 dbp->server_val[i].ep.ep_val[j].family = NULL;
258 if (serv_val[i].ep.ep_val[j].proto)
259 dbp->server_val[i].ep.ep_val[j].proto =
260 strdup (serv_val[i].ep.ep_val[j].proto);
261 else
262 dbp->server_val[i].ep.ep_val[j].proto = NULL;
263 }
264 }
265 else
266 dbp->server_val[i].ep.ep_val = NULL;
267 dbp->server_val[i].key_type = serv_val[i].key_type;
268 dbp->server_val[i].pkey.n_len = serv_val[i].pkey.n_len;
269 if (serv_val[i].pkey.n_len > 0)
270 {
271 dbp->server_val[i].pkey.n_bytes =
272 malloc (serv_val[i].pkey.n_len);
273 if (dbp->server_val[i].pkey.n_bytes == NULL)
274 return NULL;
275 memcpy (dbp->server_val[i].pkey.n_bytes, serv_val[i].pkey.n_bytes,
276 serv_val[i].pkey.n_len);
277 }
278 else
279 dbp->server_val[i].pkey.n_bytes = NULL;
280 }
281
282 dbp->class = -1;
283 if (cinfo != NULL && cinfo->server_used >= 0)
284 {
285 dbp->server_used = cinfo->server_used;
286 dbp->current_ep = cinfo->current_ep;
287 dbp->class = cinfo->class;
288 }
289 else if (__nis_findfastest (dbp) < 1)
290 {
291 __bind_destroy (dbp);
292 return NULL;
293 }
294
295 return dbp;
296 }
297
298 nis_error
299 __do_niscall2 (const nis_server *server, u_int server_len, u_long prog,
300 xdrproc_t xargs, caddr_t req, xdrproc_t xres, caddr_t resp,
301 u_long flags, nis_cb *cb, cache2_info *cinfo)
302 {
303 enum clnt_stat result;
304 nis_error retcode;
305 dir_binding *dbp;
306
307 if (flags & MASTER_ONLY)
308 server_len = 1;
309
310 dbp = __bind_create (server, server_len, flags, cinfo);
311 if (dbp == NULL)
312 return NIS_NAMEUNREACHABLE;
313 while (__bind_connect (dbp) != NIS_SUCCESS)
314 {
315 if (__bind_next (dbp) != NIS_SUCCESS)
316 {
317 __bind_destroy (dbp);
318 return NIS_NAMEUNREACHABLE;
319 }
320 }
321
322 do
323 {
324 again:
325 result = clnt_call (dbp->clnt, prog, xargs, req, xres, resp, RPCTIMEOUT);
326
327 if (result != RPC_SUCCESS)
328 {
329 __bind_destroy (dbp);
330 retcode = NIS_RPCERROR;
331 }
332 else
333 {
334 switch (prog)
335 {
336 case NIS_IBLIST:
337 if ((((nis_result *)resp)->status == NIS_CBRESULTS) &&
338 (cb != NULL))
339 {
340 __nis_do_callback(dbp, &((nis_result *)resp)->cookie, cb);
341 break;
342 }
343 /* Yes, this is correct. If we doesn't have to start
344 a callback, look if we have to search another server */
345 case NIS_LOOKUP:
346 case NIS_ADD:
347 case NIS_MODIFY:
348 case NIS_REMOVE:
349 case NIS_IBADD:
350 case NIS_IBMODIFY:
351 case NIS_IBREMOVE:
352 case NIS_IBFIRST:
353 case NIS_IBNEXT:
354 if ((((nis_result *)resp)->status == NIS_NOTFOUND) ||
355 (((nis_result *)resp)->status == NIS_NOSUCHNAME) ||
356 (((nis_result *)resp)->status == NIS_NOT_ME))
357 {
358 if (__bind_next (dbp) == NIS_SUCCESS)
359 {
360 while (__bind_connect (dbp) != NIS_SUCCESS)
361 {
362 if (__bind_next (dbp) != NIS_SUCCESS)
363 {
364 __bind_destroy (dbp);
365 return NIS_SUCCESS;
366 }
367 }
368 }
369 else
370 break; /* No more servers to search in */
371 goto again;
372 }
373 break;
374 case NIS_FINDDIRECTORY:
375 if ((((fd_result *)resp)->status == NIS_NOTFOUND) ||
376 (((fd_result *)resp)->status == NIS_NOSUCHNAME) ||
377 (((fd_result *)resp)->status == NIS_NOT_ME))
378 {
379 if (__bind_next (dbp) == NIS_SUCCESS)
380 {
381 while (__bind_connect (dbp) != NIS_SUCCESS)
382 {
383 if (__bind_next (dbp) != NIS_SUCCESS)
384 {
385 __bind_destroy (dbp);
386 return NIS_SUCCESS;
387 }
388 }
389 }
390 else
391 break; /* No more servers to search in */
392 goto again;
393 }
394 break;
395 case NIS_DUMPLOG: /* log_result */
396 case NIS_DUMP:
397 if ((((log_result *)resp)->lr_status == NIS_NOTFOUND) ||
398 (((log_result *)resp)->lr_status == NIS_NOSUCHNAME) ||
399 (((log_result *)resp)->lr_status == NIS_NOT_ME))
400 {
401 if (__bind_next (dbp) == NIS_SUCCESS)
402 {
403 while (__bind_connect (dbp) != NIS_SUCCESS)
404 {
405 if (__bind_next (dbp) != NIS_SUCCESS)
406 {
407 __bind_destroy (dbp);
408 return NIS_SUCCESS;
409 }
410 }
411 }
412 else
413 break; /* No more servers to search in */
414 goto again;
415 }
416 break;
417 default:
418 break;
419 }
420 __bind_destroy (dbp);
421 retcode = NIS_SUCCESS;
422 }
423 }
424 while ((flags & HARD_LOOKUP) && retcode == NIS_RPCERROR);
425
426 return retcode;
427 }
428
429 static directory_obj *
430 rec_dirsearch (const_nis_name name, directory_obj *dir, u_long flags,
431 nis_error *status)
432 {
433 fd_result *fd_res;
434 XDR xdrs;
435
436 switch (nis_dir_cmp (name, dir->do_name))
437 {
438 case SAME_NAME:
439 *status = NIS_SUCCESS;
440 return dir;
441 case NOT_SEQUENTIAL:
442 /* NOT_SEQUENTIAL means, go one up and try it there ! */
443 case HIGHER_NAME:
444 { /* We need data from a parent domain */
445 directory_obj *obj;
446 char ndomain [strlen (name) + 3];
447
448 nis_domain_of_r (dir->do_name, ndomain, sizeof (ndomain));
449
450 /* The root server of our domain is a replica of the parent
451 domain ! (Now I understand why a root server must be a
452 replica of the parent domain) */
453 fd_res = __nis_finddirectory (dir, ndomain);
454 *status = fd_res->status;
455 if (fd_res->status != NIS_SUCCESS)
456 {
457 /* Try the current directory obj, maybe it works */
458 __free_fdresult (fd_res);
459 return dir;
460 }
461 obj = calloc(1, sizeof(directory_obj));
462 xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
463 fd_res->dir_data.dir_data_len, XDR_DECODE);
464 xdr_directory_obj(&xdrs, obj);
465 xdr_destroy(&xdrs);
466 __free_fdresult (fd_res);
467 if (obj != NULL)
468 {
469 /* We have found a NIS+ server serving ndomain, now
470 let us search for "name" */
471 nis_free_directory (dir);
472 return rec_dirsearch (name, obj, flags, status);
473 }
474 else
475 {
476 /* Ups, very bad. Are we already the root server ? */
477 nis_free_directory (dir);
478 return NULL;
479 }
480 }
481 break;
482 case LOWER_NAME:
483 {
484 directory_obj *obj;
485 char leaf [strlen (name) + 3];
486 char domain [strlen (name) + 3];
487 char ndomain [strlen (name) + 3];
488 char *cp;
489 u_int run = 0;
490
491 strcpy (domain, name);
492
493 do
494 {
495 if (strlen (domain) == 0)
496 {
497 nis_free_directory (dir);
498 return NULL;
499 }
500 nis_leaf_of_r (domain, leaf, sizeof (leaf));
501 nis_domain_of_r (domain, ndomain, sizeof (ndomain));
502 strcpy (domain, ndomain);
503 ++run;
504 }
505 while (nis_dir_cmp (domain, dir->do_name) != SAME_NAME);
506
507 if (run == 1)
508 {
509 /* We have found the directory above. Use it. */
510 return dir;
511 }
512
513 cp = strchr (leaf, '\0');
514 *cp++ = '.';
515 strcpy (cp, domain);
516
517 fd_res = __nis_finddirectory (dir, leaf);
518 *status = fd_res->status;
519 if (fd_res->status != NIS_SUCCESS)
520 {
521 /* Try the current directory object, maybe it works */
522 __free_fdresult (fd_res);
523 return dir;
524 }
525 obj = calloc(1, sizeof(directory_obj));
526 xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
527 fd_res->dir_data.dir_data_len, XDR_DECODE);
528 xdr_directory_obj(&xdrs, obj);
529 xdr_destroy(&xdrs);
530 __free_fdresult (fd_res);
531 if (obj != NULL)
532 {
533 /* We have found a NIS+ server serving ndomain, now
534 let us search for "name" */
535 nis_free_directory (dir);
536 return rec_dirsearch (name, obj, flags, status);
537 }
538 }
539 break;
540 case BAD_NAME:
541 nis_free_directory (dir);
542 *status = NIS_BADNAME;
543 return NULL;
544 }
545 nis_free_directory (dir);
546 *status = NIS_FAIL;
547 return NULL;
548 }
549
550 /* We try to query the current server for the searched object,
551 maybe he know about it ? */
552 static directory_obj *
553 first_shoot (const_nis_name name, directory_obj *dir, u_long flags)
554 {
555 directory_obj *obj;
556 fd_result *fd_res;
557 XDR xdrs;
558 char domain [strlen (name) + 3];
559
560 if (nis_dir_cmp (name, dir->do_name) == SAME_NAME)
561 return dir;
562
563 nis_domain_of_r (name, domain, sizeof (domain));
564
565 if (nis_dir_cmp (domain, dir->do_name) == SAME_NAME)
566 return dir;
567
568 fd_res = __nis_finddirectory (dir, domain);
569 if (fd_res->status != NIS_SUCCESS)
570 {
571 __free_fdresult (fd_res);
572 return NULL;
573 }
574 obj = calloc(1, sizeof(directory_obj));
575 if (obj == NULL)
576 return NULL;
577 xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
578 fd_res->dir_data.dir_data_len, XDR_DECODE);
579 xdr_directory_obj(&xdrs, obj);
580 xdr_destroy(&xdrs);
581 __free_fdresult (fd_res);
582 if (obj != NULL)
583 {
584 nis_free_directory (dir);
585 return obj;
586 }
587 return NULL;
588 }
589
590 nis_error
591 __do_niscall (const_nis_name name, u_long prog, xdrproc_t xargs,
592 caddr_t req, xdrproc_t xres, caddr_t resp, u_long flags,
593 nis_cb *cb)
594 {
595 nis_error retcode;
596 directory_obj *dir = NULL;
597 nis_server *server;
598 u_int server_len;
599 cache2_info cinfo = {-1, -1, -1};
600 int saved_errno = errno;
601
602 if (name == NULL)
603 return NIS_BADNAME;
604
605 /* Search in local cache. In the moment, we ignore the fastest server */
606 if (!(flags & NO_CACHE))
607 dir = __nis_cache_search (name, flags, &cinfo);
608
609 if (dir == NULL)
610 {
611 nis_error status;
612 directory_obj *obj;
613
614 dir = readColdStartFile ();
615 if (dir == NULL) /* No /var/nis/NIS_COLD_START->no NIS+ installed */
616 {
617 __set_errno (saved_errno);
618 return NIS_UNAVAIL;
619 }
620
621 /* Try at first, if servers in "dir" know our object */
622 obj = first_shoot (name, dir, flags);
623 if (obj == NULL)
624 {
625 dir = rec_dirsearch (name, dir, flags, &status);
626 if (dir == NULL)
627 {
628 __set_errno (saved_errno);
629 return status;
630 }
631 }
632 else
633 dir = obj;
634 }
635
636 if (flags & MASTER_ONLY)
637 {
638 server = dir->do_servers.do_servers_val;
639 server_len = 1;
640 }
641 else
642 {
643 server = dir->do_servers.do_servers_val;
644 server_len = dir->do_servers.do_servers_len;
645 }
646
647
648 retcode = __do_niscall2 (server, server_len, prog, xargs, req, xres, resp,
649 flags, cb, &cinfo);
650
651 nis_free_directory (dir);
652
653 __set_errno (saved_errno);
654
655 return retcode;
656 }