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
26 #include <rpcsvc/nis.h>
27 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
32 #include "nis_intern.h"
35 static const struct timeval RPCTIMEOUT
= {10, 0};
36 static const struct timeval UDPTIMEOUT
= {5, 0};
38 extern u_short
__pmap_getnisport (struct sockaddr_in
*address
, u_long program
,
39 u_long version
, u_int protocol
);
42 inetstr2int (const char *str
)
45 for (size_t i
= 0; str
[i
] != '\0'; ++i
)
46 if (str
[i
] == '.' && __builtin_expect (++j
== 4, 0))
50 return inet_addr (memcpy (buffer
, str
, i
));
53 return inet_addr (str
);
57 __nisbind_destroy (dir_binding
*bind
)
59 if (bind
->clnt
!= NULL
)
62 auth_destroy (bind
->clnt
->cl_auth
);
63 clnt_destroy (bind
->clnt
);
66 libnsl_hidden_def (__nisbind_destroy
)
69 __nisbind_next (dir_binding
*bind
)
71 if (bind
->clnt
!= NULL
)
74 auth_destroy (bind
->clnt
->cl_auth
);
75 clnt_destroy (bind
->clnt
);
79 if (bind
->trys
>= bind
->server_len
)
82 for (u_int j
= bind
->current_ep
+ 1;
83 j
< bind
->server_val
[bind
->server_used
].ep
.ep_len
; ++j
)
84 if (strcmp (bind
->server_val
[bind
->server_used
].ep
.ep_val
[j
].family
,
86 if (bind
->server_val
[bind
->server_used
].ep
.ep_val
[j
].proto
[0] == '-')
94 if (bind
->server_used
>= bind
->server_len
)
95 bind
->server_used
= 0;
97 for (u_int j
= 0; j
< bind
->server_val
[bind
->server_used
].ep
.ep_len
; ++j
)
98 if (strcmp (bind
->server_val
[bind
->server_used
].ep
.ep_val
[j
].family
,
100 if (bind
->server_val
[bind
->server_used
].ep
.ep_val
[j
].proto
[0] == '-')
102 bind
->current_ep
= j
;
108 libnsl_hidden_def (__nisbind_next
)
111 __nisbind_connect (dir_binding
*dbp
)
118 serv
= &dbp
->server_val
[dbp
->server_used
];
120 memset (&dbp
->addr
, '\0', sizeof (dbp
->addr
));
121 dbp
->addr
.sin_family
= AF_INET
;
123 dbp
->addr
.sin_addr
.s_addr
=
124 inetstr2int (serv
->ep
.ep_val
[dbp
->current_ep
].uaddr
);
126 if (dbp
->addr
.sin_addr
.s_addr
== INADDR_NONE
)
129 /* Check, if the host is online and rpc.nisd is running. Much faster
130 then the clnt*_create functions: */
131 if (__pmap_getnisport (&dbp
->addr
, NIS_PROG
, NIS_VERSION
, IPPROTO_UDP
) == 0)
134 dbp
->socket
= RPC_ANYSOCK
;
136 dbp
->clnt
= clntudp_create (&dbp
->addr
, NIS_PROG
, NIS_VERSION
,
137 UDPTIMEOUT
, &dbp
->socket
);
139 dbp
->clnt
= clnttcp_create (&dbp
->addr
, NIS_PROG
, NIS_VERSION
,
142 if (dbp
->clnt
== NULL
)
145 clnt_control (dbp
->clnt
, CLSET_TIMEOUT
, (caddr_t
) &RPCTIMEOUT
);
146 /* If the program exists, close the socket */
147 if (fcntl (dbp
->socket
, F_SETFD
, 1) == -1)
148 perror ("fcntl: F_SETFD");
152 if (serv
->key_type
== NIS_PK_DH
)
154 char netname
[MAXNETNAMELEN
+ 1];
157 p
= stpcpy (netname
, "unix.");
158 strncpy (p
, serv
->name
, MAXNETNAMELEN
- 5);
159 netname
[MAXNETNAMELEN
] = '\0';
160 // XXX What is this supposed to do? If we really want to replace
161 // XXX the first dot, then we might as well use unix@ as the
162 // XXX prefix string. --drepper
163 p
= strchr (netname
, '.');
166 authdes_pk_create (netname
, &serv
->pkey
, 300, NULL
, NULL
);
167 if (!dbp
->clnt
->cl_auth
)
168 dbp
->clnt
->cl_auth
= authunix_create_default ();
171 dbp
->clnt
->cl_auth
= authunix_create_default ();
176 libnsl_hidden_def (__nisbind_connect
)
179 __nisbind_create (dir_binding
*dbp
, const nis_server
*serv_val
,
180 unsigned int serv_len
, unsigned int flags
)
184 dbp
->server_len
= serv_len
;
185 dbp
->server_val
= (nis_server
*)serv_val
;
187 if (flags
& USE_DGRAM
)
190 dbp
->use_udp
= FALSE
;
192 if (flags
& NO_AUTHINFO
)
193 dbp
->use_auth
= FALSE
;
195 dbp
->use_auth
= TRUE
;
197 if (flags
& MASTER_ONLY
)
198 dbp
->master_only
= TRUE
;
200 dbp
->master_only
= FALSE
;
202 /* We try the first server */
206 if (__nis_findfastest (dbp
) < 1)
207 return NIS_NAMEUNREACHABLE
;
211 libnsl_hidden_def (__nisbind_create
)
213 /* __nisbind_connect (dbp) must be run before calling this function !
214 So we could use the same binding twice */
216 __do_niscall3 (dir_binding
*dbp
, u_long prog
, xdrproc_t xargs
, caddr_t req
,
217 xdrproc_t xres
, caddr_t resp
, unsigned int flags
, nis_cb
*cb
)
219 enum clnt_stat result
;
223 return NIS_NAMEUNREACHABLE
;
228 result
= clnt_call (dbp
->clnt
, prog
, xargs
, req
, xres
, resp
, RPCTIMEOUT
);
230 if (result
!= RPC_SUCCESS
)
231 retcode
= NIS_RPCERROR
;
237 if ((((nis_result
*)resp
)->status
== NIS_CBRESULTS
) &&
240 __nis_do_callback (dbp
, &((nis_result
*) resp
)->cookie
, cb
);
243 /* Yes, the missing break is correct. If we doesn't have to
244 start a callback, look if we have to search another server */
254 if (((nis_result
*)resp
)->status
== NIS_SYSTEMERROR
255 || ((nis_result
*)resp
)->status
== NIS_NOSUCHNAME
256 || ((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
)
268 break; /* No more servers to search in */
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
)
278 case NIS_DUMPLOG
: /* log_result */
280 if (((log_result
*)resp
)->lr_status
== NIS_SYSTEMERROR
281 || ((log_result
*)resp
)->lr_status
== NIS_NOSUCHNAME
282 || ((log_result
*)resp
)->lr_status
== NIS_NOT_ME
)
288 retcode
= NIS_SUCCESS
;
291 while ((flags
& HARD_LOOKUP
) && retcode
== NIS_RPCERROR
);
295 libnsl_hidden_def (__do_niscall3
)
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 unsigned int flags
, nis_cb
*cb
)
306 if (flags
& MASTER_ONLY
)
309 status
= __nisbind_create (&dbp
, server
, server_len
, flags
);
310 if (status
!= NIS_SUCCESS
)
313 while (__nisbind_connect (&dbp
) != NIS_SUCCESS
)
314 if (__nisbind_next (&dbp
) != NIS_SUCCESS
)
315 return NIS_NAMEUNREACHABLE
;
317 status
= __do_niscall3 (&dbp
, prog
, xargs
, req
, xres
, resp
, flags
, cb
);
319 __nisbind_destroy (&dbp
);
325 static directory_obj
*
326 rec_dirsearch (const_nis_name name
, directory_obj
*dir
, nis_error
*status
)
331 switch (nis_dir_cmp (name
, dir
->do_name
))
334 *status
= NIS_SUCCESS
;
337 /* NOT_SEQUENTIAL means, go one up and try it there ! */
339 { /* We need data from a parent domain */
341 char ndomain
[strlen (dir
->do_name
) + 3];
343 nis_domain_of_r (dir
->do_name
, ndomain
, sizeof (ndomain
));
345 /* The root server of our domain is a replica of the parent
346 domain ! (Now I understand why a root server must be a
347 replica of the parent domain) */
348 fd_res
= __nis_finddirectory (dir
, ndomain
);
351 nis_free_directory (dir
);
352 *status
= NIS_NOMEMORY
;
355 *status
= fd_res
->status
;
356 if (fd_res
->status
!= NIS_SUCCESS
)
358 /* Try the current directory obj, maybe it works */
359 __free_fdresult (fd_res
);
362 nis_free_directory (dir
);
363 obj
= calloc (1, sizeof (directory_obj
));
366 __free_fdresult (fd_res
);
367 *status
= NIS_NOMEMORY
;
370 xdrmem_create (&xdrs
, fd_res
->dir_data
.dir_data_val
,
371 fd_res
->dir_data
.dir_data_len
, XDR_DECODE
);
372 _xdr_directory_obj (&xdrs
, obj
);
374 __free_fdresult (fd_res
);
376 /* We have found a NIS+ server serving ndomain, now
377 let us search for "name" */
378 return rec_dirsearch (name
, obj
, status
);
384 size_t namelen
= strlen (name
);
385 char leaf
[namelen
+ 3];
386 char domain
[namelen
+ 3];
387 char ndomain
[namelen
+ 3];
390 strcpy (domain
, name
);
394 if (domain
[0] == '\0')
396 nis_free_directory (dir
);
399 nis_leaf_of_r (domain
, leaf
, sizeof (leaf
));
400 nis_domain_of_r (domain
, ndomain
, sizeof (ndomain
));
401 strcpy (domain
, ndomain
);
403 while (nis_dir_cmp (domain
, dir
->do_name
) != SAME_NAME
);
405 cp
= rawmemchr (leaf
, '\0');
409 fd_res
= __nis_finddirectory (dir
, leaf
);
412 nis_free_directory (dir
);
413 *status
= NIS_NOMEMORY
;
416 *status
= fd_res
->status
;
417 if (fd_res
->status
!= NIS_SUCCESS
)
419 /* Try the current directory object, maybe it works */
420 __free_fdresult (fd_res
);
423 nis_free_directory (dir
);
424 obj
= calloc (1, sizeof(directory_obj
));
427 __free_fdresult (fd_res
);
428 *status
= NIS_NOMEMORY
;
431 xdrmem_create (&xdrs
, fd_res
->dir_data
.dir_data_val
,
432 fd_res
->dir_data
.dir_data_len
, XDR_DECODE
);
433 _xdr_directory_obj (&xdrs
, obj
);
435 __free_fdresult (fd_res
);
436 /* We have found a NIS+ server serving ndomain, now
437 let us search for "name" */
438 return rec_dirsearch (name
, obj
, status
);
442 nis_free_directory (dir
);
443 *status
= NIS_BADNAME
;
446 nis_free_directory (dir
);
451 /* We try to query the current server for the searched object,
452 maybe he know about it ? */
453 static directory_obj
*
454 first_shoot (const_nis_name name
, int search_parent_first
, directory_obj
*dir
)
456 directory_obj
*obj
= NULL
;
459 char domain
[strlen (name
) + 3];
462 if (nis_dir_cmp (name
, dir
->do_name
) == SAME_NAME
)
466 const char *search_name
= name
;
467 if (search_parent_first
)
469 nis_domain_of_r (name
, domain
, sizeof (domain
));
470 search_name
= domain
;
473 if (nis_dir_cmp (search_name
, dir
->do_name
) == SAME_NAME
)
476 fd_res
= __nis_finddirectory (dir
, search_name
);
479 if (fd_res
->status
== NIS_SUCCESS
480 && (obj
= calloc (1, sizeof (directory_obj
))) != NULL
)
482 xdrmem_create (&xdrs
, fd_res
->dir_data
.dir_data_val
,
483 fd_res
->dir_data
.dir_data_len
, XDR_DECODE
);
484 _xdr_directory_obj (&xdrs
, obj
);
487 if (strcmp (dir
->do_name
, obj
->do_name
) != 0)
489 nis_free_directory (obj
);
494 __free_fdresult (fd_res
);
497 nis_free_directory (dir
);
503 __nisfind_server (const_nis_name name
, int search_parent_first
,
510 /* Search in local cache. In the moment, we ignore the fastest server */
511 if (!(flags
& NO_CACHE
))
512 dir
= __nis_cache_search (name
, flags
, &cinfo
);
515 nis_error result
= NIS_SUCCESS
;
521 *dir
= readColdStartFile ();
523 /* No /var/nis/NIS_COLD_START->no NIS+ installed. */
526 /* Try at first, if servers in "dir" know our object */
527 obj
= first_shoot (name
, search_parent_first
, *dir
);
530 obj
= rec_dirsearch (name
, *dir
, &status
);
543 __prepare_niscall (const_nis_name name
, directory_obj
**dirp
,
544 dir_binding
*bptrp
, unsigned int flags
)
546 nis_error retcode
= __nisfind_server (name
, 1, dirp
);
547 if (__builtin_expect (retcode
!= NIS_SUCCESS
, 0))
553 if (flags
& MASTER_ONLY
)
555 server
= (*dirp
)->do_servers
.do_servers_val
;
560 server
= (*dirp
)->do_servers
.do_servers_val
;
561 server_len
= (*dirp
)->do_servers
.do_servers_len
;
564 retcode
= __nisbind_create (bptrp
, server
, server_len
, flags
);
565 if (retcode
== NIS_SUCCESS
)
568 if (__nisbind_connect (bptrp
) == NIS_SUCCESS
)
570 while (__nisbind_next (bptrp
) == NIS_SUCCESS
);
572 __nisbind_destroy (bptrp
);
573 memset (bptrp
, '\0', sizeof (*bptrp
));
575 retcode
= NIS_NAMEUNREACHABLE
;
578 nis_free_directory (*dirp
);
583 libnsl_hidden_def (__prepare_niscall
)
587 __do_niscall (const_nis_name name
, u_long prog
, xdrproc_t xargs
,
588 caddr_t req
, xdrproc_t xres
, caddr_t resp
, unsigned int flags
,
592 directory_obj
*dir
= NULL
;
593 int saved_errno
= errno
;
595 nis_error retcode
= __prepare_niscall (name
, &dir
, &bptr
, flags
);
596 if (retcode
== NIS_SUCCESS
)
598 retcode
= __do_niscall3 (&bptr
, prog
, xargs
, req
, xres
, resp
, flags
, cb
);
600 __nisbind_destroy (&bptr
);
602 nis_free_directory (dir
);
605 __set_errno (saved_errno
);