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