672755055b65c53844fdb40c4f95747cdb1e587d
[kopensolaris-gnu/glibc.git] / nis / nis_call.c
1 /* Copyright (C) 1997 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 TIMEOUT = {10, 0};
31
32 unsigned long
33 inetstr2int (const char *str)
34 {
35   char buffer[strlen (str) + 3];
36   size_t buflen;
37   size_t i, j;
38
39   buflen = stpcpy (buffer, str) - buffer;
40
41   j = 0;
42   for (i = 0; i < buflen; ++i)
43     if (buffer[i] == '.')
44       {
45         ++j;
46         if (j == 4)
47           {
48             buffer[i] = '\0';
49             break;
50           }
51       }
52
53   return inet_addr (buffer);
54 }
55
56 static void
57 __bind_destroy (dir_binding *bind)
58 {
59   if (bind->clnt != NULL)
60     {
61       if (bind->use_auth)
62         auth_destroy (bind->clnt->cl_auth);
63       clnt_destroy (bind->clnt);
64     }
65   free (bind->server_val);
66   free (bind);
67 }
68
69 static nis_error
70 __bind_next (dir_binding *bind)
71 {
72   u_int j;
73
74   if (bind->clnt != NULL)
75     {
76       if (bind->use_auth)
77         auth_destroy (bind->clnt->cl_auth);
78       clnt_destroy (bind->clnt);
79       bind->clnt = NULL;
80     }
81
82   if (bind->trys >= bind->server_len)
83     return NIS_FAIL;
84
85   for (j = bind->current_ep + 1;
86        j < bind->server_val[bind->server_used].ep.ep_len; ++j)
87     if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
88                 "inet") == 0)
89       if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].proto,
90                   "-") == 0)
91         {
92           bind->current_ep = j;
93           return NIS_SUCCESS;
94         }
95
96   ++bind->trys;
97   ++bind->server_used;
98   if (bind->server_used >= bind->server_len)
99     bind->server_used = 0;
100
101   for (j = bind->current_ep + 1;
102        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                                  TIMEOUT, &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)&TIMEOUT);
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 {
191   dir_binding *dbp;
192   u_int i;
193
194   dbp = calloc (1, sizeof (dir_binding));
195   if (dbp == NULL)
196     return NULL;
197
198   dbp->server_len = serv_len;
199   dbp->server_val = calloc (1, sizeof (nis_server) * serv_len);
200   if (dbp->server_val == NULL)
201     {
202       free (dbp);
203       return NULL;
204     }
205
206   if (flags & USE_DGRAM)
207     dbp->use_udp = TRUE;
208   else
209     dbp->use_udp = FALSE;
210
211   if (flags & NO_AUTHINFO)
212     dbp->use_auth = FALSE;
213   else
214     dbp->use_auth = TRUE;
215
216   if (flags & MASTER_ONLY)
217     dbp->master_only = TRUE;
218   else
219     dbp->master_only = FALSE;
220
221   dbp->trys = 1;
222
223   for (i = 0; i < serv_len; ++i)
224     {
225       if (serv_val[i].name != NULL)
226         dbp->server_val[i].name = strdup (serv_val[i].name);
227
228       dbp->server_val[i].ep.ep_len = serv_val[i].ep.ep_len;
229       if (dbp->server_val[i].ep.ep_len > 0)
230         {
231           unsigned long j;
232
233           dbp->server_val[i].ep.ep_val =
234             malloc (serv_val[i].ep.ep_len * sizeof (endpoint));
235           for (j = 0; j < dbp->server_val[i].ep.ep_len; ++j)
236             {
237               if (serv_val[i].ep.ep_val[j].uaddr)
238                 dbp->server_val[i].ep.ep_val[j].uaddr =
239                   strdup (serv_val[i].ep.ep_val[j].uaddr);
240               else
241                 dbp->server_val[i].ep.ep_val[j].uaddr = NULL;
242               if (serv_val[i].ep.ep_val[j].family)
243                 dbp->server_val[i].ep.ep_val[j].family =
244                   strdup (serv_val[i].ep.ep_val[j].family);
245               else
246                 dbp->server_val[i].ep.ep_val[j].family = NULL;
247               if (serv_val[i].ep.ep_val[j].proto)
248                 dbp->server_val[i].ep.ep_val[j].proto =
249                   strdup (serv_val[i].ep.ep_val[j].proto);
250               else
251                 dbp->server_val[i].ep.ep_val[j].proto = NULL;
252             }
253         }
254       else
255         dbp->server_val[i].ep.ep_val = NULL;
256       dbp->server_val[i].key_type = serv_val[i].key_type;
257       dbp->server_val[i].pkey.n_len = serv_val[i].pkey.n_len;
258       if (serv_val[i].pkey.n_len > 0)
259         {
260           dbp->server_val[i].pkey.n_bytes =
261             malloc (serv_val[i].pkey.n_len);
262           if (dbp->server_val[i].pkey.n_bytes == NULL)
263             return NULL;
264           memcpy (dbp->server_val[i].pkey.n_bytes, serv_val[i].pkey.n_bytes,
265                   serv_val[i].pkey.n_len);
266         }
267       else
268         dbp->server_val[i].pkey.n_bytes = NULL;
269     }
270
271   if (__nis_findfastest (dbp) < 1)
272     {
273       __bind_destroy (dbp);
274       return NULL;
275     }
276
277   return dbp;
278 }
279
280 nis_error
281 __do_niscall2 (const nis_server *server, u_int server_len, u_long prog,
282                xdrproc_t xargs, caddr_t req, xdrproc_t xres, caddr_t resp,
283                u_long flags)
284 {
285   enum clnt_stat result;
286   nis_error retcode;
287   dir_binding *dbp;
288
289   if (flags & MASTER_ONLY)
290     server_len = 1;
291
292   if ((dbp = __bind_create (server, server_len, flags)) == NULL)
293     return NIS_UNAVAIL;
294   while (__bind_connect (dbp) != NIS_SUCCESS)
295     {
296       if (__bind_next (dbp) != NIS_SUCCESS)
297         {
298           __bind_destroy (dbp);
299           return NIS_NAMEUNREACHABLE;
300         }
301     }
302
303   do
304     {
305     again:
306       result = clnt_call (dbp->clnt, prog, xargs, req, xres, resp, TIMEOUT);
307
308       if (result != RPC_SUCCESS)
309         {
310           clnt_perror (dbp->clnt, "__do_niscall2: clnt_call");
311           __bind_destroy (dbp);
312           retcode = NIS_RPCERROR;
313         }
314       else
315         {
316           switch (prog)
317             {
318             case NIS_LOOKUP:
319             case NIS_ADD:
320             case NIS_MODIFY:
321             case NIS_REMOVE:
322             case NIS_IBLIST:
323             case NIS_IBADD:
324             case NIS_IBMODIFY:
325             case NIS_IBREMOVE:
326             case NIS_IBFIRST:
327             case NIS_IBNEXT:
328               if ((((nis_result *)xres)->status == NIS_NOTFOUND) ||
329                   (((nis_result *)xres)->status == NIS_NOSUCHNAME) ||
330                   (((nis_result *)xres)->status == NIS_NOT_ME))
331                 {
332                   if (__bind_next (dbp) == NIS_SUCCESS)
333                     while (__bind_connect (dbp) != NIS_SUCCESS)
334                       {
335                         if (__bind_next (dbp) != NIS_SUCCESS)
336                           {
337                             __bind_destroy (dbp);
338                             return NIS_SUCCESS;
339                           }
340                       }
341                   goto again;
342                 }
343             case NIS_FINDDIRECTORY:
344               if ((((fd_result *)xres)->status == NIS_NOTFOUND) ||
345                   (((fd_result *)xres)->status == NIS_NOSUCHNAME) ||
346                   (((fd_result *)xres)->status == NIS_NOT_ME))
347                 {
348                   if (__bind_next (dbp) == NIS_SUCCESS)
349                     while (__bind_connect (dbp) != NIS_SUCCESS)
350                       {
351                         if (__bind_next (dbp) != NIS_SUCCESS)
352                           {
353                             __bind_destroy (dbp);
354                             return NIS_SUCCESS;
355                           }
356                       }
357                   goto again;
358                 }
359               break;
360             case NIS_DUMPLOG: /* log_result */
361             case NIS_DUMP:
362               if ((((log_result *)xres)->lr_status == NIS_NOTFOUND) ||
363                   (((log_result *)xres)->lr_status == NIS_NOSUCHNAME) ||
364                   (((log_result *)xres)->lr_status == NIS_NOT_ME))
365                 {
366                   if (__bind_next (dbp) == NIS_SUCCESS)
367                     while (__bind_connect (dbp) != NIS_SUCCESS)
368                       {
369                         if (__bind_next (dbp) != NIS_SUCCESS)
370                           {
371                             __bind_destroy (dbp);
372                             return NIS_SUCCESS;
373                           }
374                       }
375                   goto again;
376                 }
377               break;
378             default:
379               break;
380             }
381           __bind_destroy (dbp);
382           retcode = NIS_SUCCESS;
383         }
384     }
385   while ((flags & HARD_LOOKUP) && retcode == NIS_RPCERROR);
386
387   return retcode;
388 }
389
390 static directory_obj *
391 rec_dirsearch (const_nis_name name, directory_obj *dir, u_long flags,
392                nis_error *status)
393 {
394   fd_result *fd_res;
395   XDR xdrs;
396   char domain [strlen (name) + 3];
397
398   nis_domain_of_r (name, domain, sizeof (domain));
399   if (strncmp (domain, "org_dir.", 8) == 0)
400     {
401       char tmp[strlen (name) + 3];
402
403       nis_domain_of_r (domain, tmp, sizeof (tmp));
404       strcpy (domain, tmp);
405     }
406   else
407     if (strncmp (domain, "groups_dir.", 11) == 0)
408       {
409         char tmp[strlen (name) + 3];
410
411         nis_domain_of_r (domain, tmp, sizeof (tmp));
412         strcpy (domain, tmp);
413       }
414     else
415       {
416         /* We have no grous_dir or org_dir, so try the complete name */
417         strcpy (domain, name);
418       }
419
420   switch (nis_dir_cmp (domain, dir->do_name))
421     {
422     case SAME_NAME:
423       *status = NIS_SUCCESS;
424       return dir;
425     case NOT_SEQUENTIAL:
426       /* NOT_SEQUENTIAL means, go one up and try it there ! */
427     case HIGHER_NAME:
428       { /* We need data from a parent domain */
429         directory_obj *obj;
430         char ndomain [strlen (name) + 3];
431
432         nis_domain_of_r (dir->do_name, ndomain, sizeof (ndomain));
433
434         /* The root server of our domain is a replica of the parent
435            domain ! (Now I understand why a root server must be a
436            replica of the parent domain) */
437         fd_res = __nis_finddirectory (dir, ndomain);
438         *status = fd_res->status;
439         if (fd_res->status != NIS_SUCCESS)
440           {
441             nis_free_directory (dir);
442             xdr_free((xdrproc_t)xdr_fd_result, (caddr_t)fd_res);
443             return NULL;
444           }
445         __cache_add (fd_res);
446         obj = calloc(1, sizeof(directory_obj));
447         xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
448                       fd_res->dir_data.dir_data_len, XDR_DECODE);
449         xdr_directory_obj(&xdrs, obj);
450         xdr_destroy(&xdrs);
451         xdr_free((xdrproc_t)xdr_fd_result, (caddr_t)fd_res);
452         if (obj != NULL)
453           {
454             /* We have found a NIS+ server serving ndomain, now
455                let us search for "name" */
456             nis_free_directory (dir);
457             return rec_dirsearch (name, obj, flags, status);
458           }
459         else
460           {
461             /* Ups, very bad. Are we already the root server ? */
462             nis_free_directory (dir);
463             return NULL;
464           }
465       }
466     break;
467     case LOWER_NAME:
468       {
469         directory_obj *obj;
470         char leaf [strlen (name) + 3];
471         char ndomain [strlen (name) + 3];
472         char *cp;
473
474         do
475           {
476             if (strlen (domain) == 0)
477               {
478                 nis_free_directory (dir);
479                 return NULL;
480               }
481             nis_leaf_of_r (domain, leaf, sizeof (leaf));
482             nis_domain_of_r (domain, ndomain, sizeof (ndomain));
483             strcpy (domain, ndomain);
484           }
485         while (nis_dir_cmp (domain, dir->do_name) != SAME_NAME);
486         cp = strchr (leaf, '\0');
487         *cp++ = '.';
488         strcpy (cp, domain);
489
490         fd_res = __nis_finddirectory (dir, leaf);
491         *status = fd_res->status;
492         if (fd_res->status != NIS_SUCCESS)
493           {
494             nis_free_directory (dir);
495             xdr_free((xdrproc_t)xdr_fd_result, (caddr_t)fd_res);
496             return NULL;
497           }
498         __cache_add (fd_res);
499         obj = calloc(1, sizeof(directory_obj));
500         xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
501                       fd_res->dir_data.dir_data_len, XDR_DECODE);
502         xdr_directory_obj(&xdrs, obj);
503         xdr_destroy(&xdrs);
504         xdr_free((xdrproc_t)xdr_fd_result, (caddr_t)fd_res);
505         if (obj != NULL)
506           {
507             /* We have found a NIS+ server serving ndomain, now
508                let us search for "name" */
509             nis_free_directory (dir);
510             return rec_dirsearch (name, obj, flags, status);
511           }
512       }
513     break;
514     case BAD_NAME:
515       nis_free_directory (dir);
516       *status = NIS_BADNAME;
517       return NULL;
518     }
519   nis_free_directory (dir);
520   *status = NIS_FAIL;
521   return NULL;
522 }
523
524 nis_error
525 __do_niscall (const_nis_name name, u_long prog, xdrproc_t xargs,
526               caddr_t req, xdrproc_t xres, caddr_t resp, u_long flags)
527 {
528   nis_error retcode;
529   directory_obj *dir = NULL;
530   nis_server *server;
531   u_int server_len;
532
533   if (name == NULL)
534     return NIS_BADNAME;
535
536   if ((flags & NO_CACHE) !=  NO_CACHE)
537     dir = __cache_search (name);
538
539   if (dir == NULL)
540     {
541       nis_error status;
542       dir = readColdStartFile ();
543       if (dir == NULL) /* No /var/nis/NIS_COLD_START->no NIS+ installed */
544         return NIS_UNAVAIL;
545
546       dir = rec_dirsearch (name, dir, flags, &status);
547       if (dir == NULL)
548         return status;
549     }
550
551   if (flags & MASTER_ONLY)
552     {
553       server = dir->do_servers.do_servers_val;
554       server_len = 1;
555     }
556   else
557     {
558       server = dir->do_servers.do_servers_val;
559       server_len = dir->do_servers.do_servers_len;
560     }
561
562
563   retcode = __do_niscall2 (server, server_len, prog, xargs, req, xres, resp,
564                            flags);
565
566   nis_free_directory (dir);
567
568   return retcode;
569 }