Set UDP resend timeout correct.
[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 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 {
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, nis_cb *cb)
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_NAMEUNREACHABLE;
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, RPCTIMEOUT);
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_IBLIST:
319               if ((((nis_result *)resp)->status == NIS_CBRESULTS) &&
320                   (cb != NULL))
321                 {
322                   __nis_do_callback(dbp, &((nis_result *)resp)->cookie, cb);
323                   break;
324                 }
325               /* Yes, this is correct. If we doesn't have to start
326                  a callback, look if we have to search another server */
327             case NIS_LOOKUP:
328             case NIS_ADD:
329             case NIS_MODIFY:
330             case NIS_REMOVE:
331             case NIS_IBADD:
332             case NIS_IBMODIFY:
333             case NIS_IBREMOVE:
334             case NIS_IBFIRST:
335             case NIS_IBNEXT:
336               if ((((nis_result *)resp)->status == NIS_NOTFOUND) ||
337                   (((nis_result *)resp)->status == NIS_NOSUCHNAME) ||
338                   (((nis_result *)resp)->status == NIS_NOT_ME))
339                 {
340                   if (__bind_next (dbp) == NIS_SUCCESS)
341                     {
342                       while (__bind_connect (dbp) != NIS_SUCCESS)
343                         {
344                           if (__bind_next (dbp) != NIS_SUCCESS)
345                             {
346                               __bind_destroy (dbp);
347                               return NIS_SUCCESS;
348                             }
349                         }
350                     }
351                   else
352                     break; /* No more servers to search in */
353                   goto again;
354                 }
355               break;
356             case NIS_FINDDIRECTORY:
357               if ((((fd_result *)resp)->status == NIS_NOTFOUND) ||
358                   (((fd_result *)resp)->status == NIS_NOSUCHNAME) ||
359                   (((fd_result *)resp)->status == NIS_NOT_ME))
360                 {
361                   if (__bind_next (dbp) == NIS_SUCCESS)
362                     {
363                       while (__bind_connect (dbp) != NIS_SUCCESS)
364                         {
365                           if (__bind_next (dbp) != NIS_SUCCESS)
366                             {
367                               __bind_destroy (dbp);
368                               return NIS_SUCCESS;
369                             }
370                         }
371                     }
372                   else
373                     break; /* No more servers to search in */
374                   goto again;
375                 }
376               break;
377             case NIS_DUMPLOG: /* log_result */
378             case NIS_DUMP:
379               if ((((log_result *)resp)->lr_status == NIS_NOTFOUND) ||
380                   (((log_result *)resp)->lr_status == NIS_NOSUCHNAME) ||
381                   (((log_result *)resp)->lr_status == NIS_NOT_ME))
382                 {
383                   if (__bind_next (dbp) == NIS_SUCCESS)
384                     {
385                       while (__bind_connect (dbp) != NIS_SUCCESS)
386                         {
387                           if (__bind_next (dbp) != NIS_SUCCESS)
388                             {
389                               __bind_destroy (dbp);
390                               return NIS_SUCCESS;
391                             }
392                         }
393                     }
394                   else
395                     break; /* No more servers to search in */
396                   goto again;
397                 }
398               break;
399             default:
400               break;
401             }
402           __bind_destroy (dbp);
403           retcode = NIS_SUCCESS;
404         }
405     }
406   while ((flags & HARD_LOOKUP) && retcode == NIS_RPCERROR);
407
408   return retcode;
409 }
410
411 static directory_obj *
412 rec_dirsearch (const_nis_name name, directory_obj *dir, u_long flags,
413                nis_error *status)
414 {
415   fd_result *fd_res;
416   XDR xdrs;
417   char domain [strlen (name) + 3];
418
419   nis_domain_of_r (name, domain, sizeof (domain));
420   if (strncmp (domain, "org_dir.", 8) == 0)
421     {
422       char tmp[strlen (name) + 3];
423
424       nis_domain_of_r (domain, tmp, sizeof (tmp));
425       strcpy (domain, tmp);
426     }
427   else
428     if (strncmp (domain, "groups_dir.", 11) == 0)
429       {
430         char tmp[strlen (name) + 3];
431
432         nis_domain_of_r (domain, tmp, sizeof (tmp));
433         strcpy (domain, tmp);
434       }
435     else
436       {
437         /* We have no grous_dir or org_dir, so try the complete name */
438         strcpy (domain, name);
439       }
440
441   switch (nis_dir_cmp (domain, dir->do_name))
442     {
443     case SAME_NAME:
444       *status = NIS_SUCCESS;
445       return dir;
446     case NOT_SEQUENTIAL:
447       /* NOT_SEQUENTIAL means, go one up and try it there ! */
448     case HIGHER_NAME:
449       { /* We need data from a parent domain */
450         directory_obj *obj;
451         char ndomain [strlen (name) + 3];
452
453         nis_domain_of_r (dir->do_name, ndomain, sizeof (ndomain));
454
455         /* The root server of our domain is a replica of the parent
456            domain ! (Now I understand why a root server must be a
457            replica of the parent domain) */
458         fd_res = __nis_finddirectory (dir, ndomain);
459         *status = fd_res->status;
460         if (fd_res->status != NIS_SUCCESS)
461           {
462             nis_free_directory (dir);
463             __free_fdresult (fd_res);
464             return NULL;
465           }
466         obj = calloc(1, sizeof(directory_obj));
467         xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
468                       fd_res->dir_data.dir_data_len, XDR_DECODE);
469         xdr_directory_obj(&xdrs, obj);
470         xdr_destroy(&xdrs);
471         __free_fdresult (fd_res);
472         if (obj != NULL)
473           {
474             /* We have found a NIS+ server serving ndomain, now
475                let us search for "name" */
476             nis_free_directory (dir);
477             return rec_dirsearch (name, obj, flags, status);
478           }
479         else
480           {
481             /* Ups, very bad. Are we already the root server ? */
482             nis_free_directory (dir);
483             return NULL;
484           }
485       }
486     break;
487     case LOWER_NAME:
488       {
489         directory_obj *obj;
490         char leaf [strlen (name) + 3];
491         char ndomain [strlen (name) + 3];
492         char *cp;
493
494         do
495           {
496             if (strlen (domain) == 0)
497               {
498                 nis_free_directory (dir);
499                 return NULL;
500               }
501             nis_leaf_of_r (domain, leaf, sizeof (leaf));
502             nis_domain_of_r (domain, ndomain, sizeof (ndomain));
503             strcpy (domain, ndomain);
504           }
505         while (nis_dir_cmp (domain, dir->do_name) != SAME_NAME);
506         cp = strchr (leaf, '\0');
507         *cp++ = '.';
508         strcpy (cp, domain);
509
510         fd_res = __nis_finddirectory (dir, leaf);
511         *status = fd_res->status;
512         if (fd_res->status != NIS_SUCCESS)
513           {
514             nis_free_directory (dir);
515             __free_fdresult (fd_res);
516             return NULL;
517           }
518         obj = calloc(1, sizeof(directory_obj));
519         xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
520                       fd_res->dir_data.dir_data_len, XDR_DECODE);
521         xdr_directory_obj(&xdrs, obj);
522         xdr_destroy(&xdrs);
523         __free_fdresult (fd_res);
524         if (obj != NULL)
525           {
526             /* We have found a NIS+ server serving ndomain, now
527                let us search for "name" */
528             nis_free_directory (dir);
529             return rec_dirsearch (name, obj, flags, status);
530           }
531       }
532     break;
533     case BAD_NAME:
534       nis_free_directory (dir);
535       *status = NIS_BADNAME;
536       return NULL;
537     }
538   nis_free_directory (dir);
539   *status = NIS_FAIL;
540   return NULL;
541 }
542
543 nis_error
544 __do_niscall (const_nis_name name, u_long prog, xdrproc_t xargs,
545               caddr_t req, xdrproc_t xres, caddr_t resp, u_long flags,
546               nis_cb *cb)
547 {
548   nis_error retcode;
549   directory_obj *dir = NULL;
550   nis_server *server;
551   u_int server_len;
552
553   if (name == NULL)
554     return NIS_BADNAME;
555
556   if (dir == NULL)
557     {
558       nis_error status;
559       dir = readColdStartFile ();
560       if (dir == NULL) /* No /var/nis/NIS_COLD_START->no NIS+ installed */
561         return NIS_UNAVAIL;
562
563       dir = rec_dirsearch (name, dir, flags, &status);
564       if (dir == NULL)
565         return status;
566     }
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
580   retcode = __do_niscall2 (server, server_len, prog, xargs, req, xres, resp,
581                            flags, cb);
582
583   nis_free_directory (dir);
584
585   return retcode;
586 }