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