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