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