Change type of first parameter of __getnetbyaddr_r.
[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 <libintl.h>
24 #include <rpc/rpc.h>
25 #include <rpc/auth.h>
26 #include <rpcsvc/nis.h>
27 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30
31 #include "nis_xdr.h"
32 #include "nis_intern.h"
33
34 static struct timeval RPCTIMEOUT = {10, 0};
35 static struct timeval UDPTIMEOUT = {5, 0};
36
37 extern u_short __pmap_getnisport (struct sockaddr_in *address, u_long program,
38                                   u_long version, u_int protocol);
39
40 unsigned long
41 inetstr2int (const char *str)
42 {
43   char buffer[strlen (str) + 3];
44   size_t buflen;
45   size_t i, j;
46
47   buflen = stpcpy (buffer, str) - buffer;
48
49   j = 0;
50   for (i = 0; i < buflen; ++i)
51     if (buffer[i] == '.')
52       {
53         ++j;
54         if (j == 4)
55           {
56             buffer[i] = '\0';
57             break;
58           }
59       }
60
61   return inet_addr (buffer);
62 }
63
64 void
65 __nisbind_destroy (dir_binding *bind)
66 {
67   if (bind->clnt != NULL)
68     {
69       if (bind->use_auth)
70         auth_destroy (bind->clnt->cl_auth);
71       clnt_destroy (bind->clnt);
72     }
73 }
74
75 nis_error
76 __nisbind_next (dir_binding *bind)
77 {
78   u_int j;
79
80   if (bind->clnt != NULL)
81     {
82       if (bind->use_auth)
83         auth_destroy (bind->clnt->cl_auth);
84       clnt_destroy (bind->clnt);
85       bind->clnt = NULL;
86     }
87
88   if (bind->trys >= bind->server_len)
89     return NIS_FAIL;
90
91   for (j = bind->current_ep + 1;
92        j < bind->server_val[bind->server_used].ep.ep_len; ++j)
93     if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
94                 "inet") == 0)
95       if (bind->server_val[bind->server_used].ep.ep_val[j].proto[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 nis_error
119 __nisbind_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)
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 nis_error
184 __nisbind_create (dir_binding *dbp, const nis_server *serv_val,
185                   unsigned int serv_len, unsigned int flags)
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 (__nis_findfastest (dbp) < 1)
212     {
213       __nisbind_destroy (dbp);
214       return NIS_NAMEUNREACHABLE;
215     }
216
217   return NIS_SUCCESS;
218 }
219
220 /* __nisbind_connect (dbp) must be run before calling this function !
221    So we could use the same binding twice */
222 nis_error
223 __do_niscall3 (dir_binding *dbp, u_long prog, xdrproc_t xargs, caddr_t req,
224                xdrproc_t xres, caddr_t resp, unsigned int flags, nis_cb *cb)
225 {
226   enum clnt_stat result;
227   nis_error retcode;
228
229   if (dbp == NULL)
230     return NIS_NAMEUNREACHABLE;
231
232   do
233     {
234     again:
235       result = clnt_call (dbp->clnt, prog, xargs, req, xres, resp, RPCTIMEOUT);
236
237       if (result != RPC_SUCCESS)
238         retcode = NIS_RPCERROR;
239       else
240         {
241           switch (prog)
242             {
243             case NIS_IBLIST:
244               if ((((nis_result *)resp)->status == NIS_CBRESULTS) &&
245                   (cb != NULL))
246                 {
247                   __nis_do_callback(dbp, &((nis_result *)resp)->cookie, cb);
248                   break;
249                 }
250               /* Yes, the missing break is correct. If we doesn't have to
251                  start a callback, look if we have to search another server */
252             case NIS_LOOKUP:
253             case NIS_ADD:
254             case NIS_MODIFY:
255             case NIS_REMOVE:
256             case NIS_IBADD:
257             case NIS_IBMODIFY:
258             case NIS_IBREMOVE:
259             case NIS_IBFIRST:
260             case NIS_IBNEXT:
261               if ((((nis_result *)resp)->status == NIS_SYSTEMERROR) ||
262                   (((nis_result *)resp)->status == NIS_NOSUCHNAME) ||
263                   (((nis_result *)resp)->status == NIS_NOT_ME))
264                 {
265                   if (__nisbind_next (dbp) == NIS_SUCCESS)
266                     {
267                       while (__nisbind_connect (dbp) != NIS_SUCCESS)
268                         {
269                           if (__nisbind_next (dbp) != NIS_SUCCESS)
270                               return NIS_SUCCESS;
271                         }
272                     }
273                   else
274                     break; /* No more servers to search in */
275                   goto again;
276                 }
277               break;
278             case NIS_FINDDIRECTORY:
279               if ((((fd_result *)resp)->status == NIS_SYSTEMERROR) ||
280                   (((fd_result *)resp)->status == NIS_NOSUCHNAME) ||
281                   (((fd_result *)resp)->status == NIS_NOT_ME))
282                 {
283                   if (__nisbind_next (dbp) == NIS_SUCCESS)
284                     {
285                       while (__nisbind_connect (dbp) != NIS_SUCCESS)
286                         {
287                           if (__nisbind_next (dbp) != NIS_SUCCESS)
288                             return NIS_SUCCESS;
289                         }
290                     }
291                   else
292                     break; /* No more servers to search in */
293                   goto again;
294                 }
295               break;
296             case NIS_DUMPLOG: /* log_result */
297             case NIS_DUMP:
298               if ((((log_result *)resp)->lr_status == NIS_SYSTEMERROR) ||
299                   (((log_result *)resp)->lr_status == NIS_NOSUCHNAME) ||
300                   (((log_result *)resp)->lr_status == NIS_NOT_ME))
301                 {
302                   if (__nisbind_next (dbp) == NIS_SUCCESS)
303                     {
304                       while (__nisbind_connect (dbp) != NIS_SUCCESS)
305                         {
306                           if (__nisbind_next (dbp) != NIS_SUCCESS)
307                             return NIS_SUCCESS;
308                         }
309                     }
310                   else
311                     break; /* No more servers to search in */
312                   goto again;
313                 }
314               break;
315             default:
316               break;
317             }
318           retcode = NIS_SUCCESS;
319         }
320     }
321   while ((flags & HARD_LOOKUP) && retcode == NIS_RPCERROR);
322
323   return retcode;
324 }
325
326 nis_error
327 __do_niscall2 (const nis_server *server, u_int server_len, u_long prog,
328                xdrproc_t xargs, caddr_t req, xdrproc_t xres, caddr_t resp,
329                unsigned int flags, nis_cb *cb)
330 {
331   dir_binding dbp;
332   nis_error status;
333
334   if (flags & MASTER_ONLY)
335     server_len = 1;
336
337   status = __nisbind_create (&dbp, server, server_len, flags);
338   if (status != NIS_SUCCESS)
339     return status;
340
341   while (__nisbind_connect (&dbp) != NIS_SUCCESS)
342     {
343       if (__nisbind_next (&dbp) != NIS_SUCCESS)
344         return NIS_NAMEUNREACHABLE;
345     }
346
347   status = __do_niscall3 (&dbp, prog, xargs, req, xres, resp, flags, cb);
348
349   __nisbind_destroy (&dbp);
350
351   return status;
352
353 }
354
355 static directory_obj *
356 rec_dirsearch (const_nis_name name, directory_obj *dir, nis_error *status)
357 {
358   fd_result *fd_res;
359   XDR xdrs;
360
361   switch (nis_dir_cmp (name, dir->do_name))
362     {
363     case SAME_NAME:
364       *status = NIS_SUCCESS;
365       return dir;
366     case NOT_SEQUENTIAL:
367       /* NOT_SEQUENTIAL means, go one up and try it there ! */
368     case HIGHER_NAME:
369       { /* We need data from a parent domain */
370         directory_obj *obj;
371         char ndomain [strlen (name) + 3];
372
373         nis_domain_of_r (dir->do_name, ndomain, sizeof (ndomain));
374
375         /* The root server of our domain is a replica of the parent
376            domain ! (Now I understand why a root server must be a
377            replica of the parent domain) */
378         fd_res = __nis_finddirectory (dir, ndomain);
379         *status = fd_res->status;
380         if (fd_res->status != NIS_SUCCESS)
381           {
382             /* Try the current directory obj, maybe it works */
383             __free_fdresult (fd_res);
384             return dir;
385           }
386         obj = calloc(1, sizeof(directory_obj));
387         xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
388                       fd_res->dir_data.dir_data_len, XDR_DECODE);
389         _xdr_directory_obj(&xdrs, obj);
390         xdr_destroy(&xdrs);
391         __free_fdresult (fd_res);
392         if (obj != NULL)
393           {
394             /* We have found a NIS+ server serving ndomain, now
395                let us search for "name" */
396             nis_free_directory (dir);
397             return rec_dirsearch (name, obj, status);
398           }
399         else
400           {
401             /* Ups, very bad. Are we already the root server ? */
402             nis_free_directory (dir);
403             return NULL;
404           }
405       }
406     break;
407     case LOWER_NAME:
408       {
409         directory_obj *obj;
410         size_t namelen = strlen (name);
411         char leaf [namelen + 3];
412         char domain [namelen + 3];
413         char ndomain [namelen + 3];
414         char *cp;
415         u_int run = 0;
416
417         strcpy (domain, name);
418
419         do
420           {
421             if (domain[0] == '\0')
422               {
423                 nis_free_directory (dir);
424                 return NULL;
425               }
426             nis_leaf_of_r (domain, leaf, sizeof (leaf));
427             nis_domain_of_r (domain, ndomain, sizeof (ndomain));
428             strcpy (domain, ndomain);
429             ++run;
430           }
431         while (nis_dir_cmp (domain, dir->do_name) != SAME_NAME);
432
433         if (run == 1)
434           {
435             /* We have found the directory above. Use it. */
436             return dir;
437           }
438
439         cp = strchr (leaf, '\0');
440         *cp++ = '.';
441         strcpy (cp, domain);
442
443         fd_res = __nis_finddirectory (dir, leaf);
444         *status = fd_res->status;
445         if (fd_res->status != NIS_SUCCESS)
446           {
447             /* Try the current directory object, maybe it works */
448             __free_fdresult (fd_res);
449             return dir;
450           }
451         obj = calloc(1, sizeof(directory_obj));
452         xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
453                       fd_res->dir_data.dir_data_len, XDR_DECODE);
454         _xdr_directory_obj(&xdrs, obj);
455         xdr_destroy(&xdrs);
456         __free_fdresult (fd_res);
457         if (obj != NULL)
458           {
459             /* We have found a NIS+ server serving ndomain, now
460                let us search for "name" */
461             nis_free_directory (dir);
462             return rec_dirsearch (name, obj, status);
463           }
464       }
465     break;
466     case BAD_NAME:
467       nis_free_directory (dir);
468       *status = NIS_BADNAME;
469       return NULL;
470     }
471   nis_free_directory (dir);
472   *status = NIS_FAIL;
473   return NULL;
474 }
475
476 /* We try to query the current server for the searched object,
477    maybe he know about it ? */
478 static directory_obj *
479 first_shoot (const_nis_name name, directory_obj *dir)
480 {
481   directory_obj *obj;
482   fd_result *fd_res;
483   XDR xdrs;
484   char domain [strlen (name) + 3];
485
486   if (nis_dir_cmp (name, dir->do_name) == SAME_NAME)
487     return dir;
488
489   nis_domain_of_r (name, domain, sizeof (domain));
490
491   if (nis_dir_cmp (domain, dir->do_name) == SAME_NAME)
492     return dir;
493
494   fd_res = __nis_finddirectory (dir, domain);
495   if (fd_res->status != NIS_SUCCESS)
496     {
497       __free_fdresult (fd_res);
498       return NULL;
499     }
500   obj = calloc(1, sizeof(directory_obj));
501   if (obj == NULL)
502     return NULL;
503   xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
504                 fd_res->dir_data.dir_data_len, XDR_DECODE);
505   _xdr_directory_obj (&xdrs, obj);
506   xdr_destroy (&xdrs);
507   __free_fdresult (fd_res);
508   if (obj != NULL)
509     {
510       nis_free_directory (dir);
511       return obj;
512     }
513   return NULL;
514 }
515
516 nis_error
517 __nisfind_server (const_nis_name name, directory_obj **dir)
518 {
519   if (name == NULL)
520     return NIS_BADNAME;
521
522 #if 0
523   /* Search in local cache. In the moment, we ignore the fastest server */
524   if (!(flags & NO_CACHE))
525     dir = __nis_cache_search (name, flags, &cinfo);
526 #endif
527
528   if (*dir == NULL)
529     {
530       nis_error status;
531       directory_obj *obj;
532
533       *dir = readColdStartFile ();
534       if (*dir == NULL) /* No /var/nis/NIS_COLD_START->no NIS+ installed */
535         return NIS_UNAVAIL;
536
537       /* Try at first, if servers in "dir" know our object */
538       obj = first_shoot (name, *dir);
539       if (obj == NULL)
540         {
541           *dir = rec_dirsearch (name, *dir, &status);
542           if (*dir == NULL)
543             return status;
544         }
545       else
546         *dir = obj;
547     }
548
549   return NIS_SUCCESS;
550 }
551
552 nis_error
553 __do_niscall (const_nis_name name, u_long prog, xdrproc_t xargs,
554               caddr_t req, xdrproc_t xres, caddr_t resp, unsigned int flags,
555               nis_cb *cb)
556 {
557   nis_error retcode;
558   dir_binding bptr;
559   directory_obj *dir = NULL;
560   nis_server *server;
561   u_int server_len;
562   int saved_errno = errno;
563
564   retcode = __nisfind_server (name, &dir);
565   if (retcode != NIS_SUCCESS)
566     return retcode;
567
568   if (flags & MASTER_ONLY)
569     {
570       server = dir->do_servers.do_servers_val;
571       server_len = 1;
572     }
573   else
574     {
575       server = dir->do_servers.do_servers_val;
576       server_len = dir->do_servers.do_servers_len;
577     }
578
579   retcode = __nisbind_create (&bptr, server, server_len, flags);
580   if (retcode == NIS_SUCCESS)
581     {
582       while (__nisbind_connect (&bptr) != NIS_SUCCESS)
583         {
584           if (__nisbind_next (&bptr) != NIS_SUCCESS)
585             {
586               nis_free_directory (dir);
587               __nisbind_destroy (&bptr);
588               return NIS_NAMEUNREACHABLE;
589             }
590         }
591       retcode = __do_niscall3 (&bptr, prog, xargs, req, xres, resp, flags, cb);
592
593       __nisbind_destroy (&bptr);
594     }
595
596   nis_free_directory (dir);
597
598   __set_errno (saved_errno);
599
600   return retcode;
601 }