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