Little optimizations.
[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 <string.h>
21 #include <rpc/rpc.h>
22 #include <rpc/auth.h>
23 #include <rpcsvc/nis.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include "nis_intern.h"
28
29 static struct timeval TIMEOUT = {25, 0};
30 static int const MAXTRIES = 3;
31
32 static 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 CLIENT *
57 __nis_dobind (const nis_server *server, u_long flags)
58 {
59   struct sockaddr_in clnt_saddr;
60   int clnt_sock;
61   size_t i;
62   CLIENT *client = NULL;
63
64   memset (&clnt_saddr, '\0', sizeof clnt_saddr);
65   clnt_saddr.sin_family = AF_INET;
66   for (i = 0; i < server->ep.ep_len; i++)
67     {
68       if (strcmp (server->ep.ep_val[i].family, "loopback") == 0)
69         {
70           if (server->ep.ep_val[i].uaddr[i] == '-')
71             clnt_saddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
72           else
73             if (strcmp (server->ep.ep_val[i].proto, "udp") == 0)
74               {
75                 if ((flags & USE_DGRAM) == USE_DGRAM)
76                   clnt_saddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
77                 else
78                   continue;
79               }
80             else
81               if (strcmp (server->ep.ep_val[i].proto, "tcp") == 0)
82                 {
83                   if ((flags & USE_DGRAM) == USE_DGRAM)
84                     continue;
85                   else
86                     clnt_saddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
87                 }
88         }
89       else
90         if (strcmp (server->ep.ep_val[i].family, "inet") == 0)
91           {
92             if (server->ep.ep_val[i].uaddr[i] == '-')
93               clnt_saddr.sin_addr.s_addr =
94                 inetstr2int (server->ep.ep_val[i].uaddr);
95             else
96               if (strcmp (server->ep.ep_val[i].proto, "udp") == 0)
97                 {
98                   if ((flags & USE_DGRAM) == USE_DGRAM)
99                     clnt_saddr.sin_addr.s_addr =
100                       inetstr2int (server->ep.ep_val[i].uaddr);
101                   else
102                     continue;
103                 }
104               else
105                 if (strcmp (server->ep.ep_val[i].proto, "tcp") == 0)
106                   {
107                     if ((flags & USE_DGRAM) == USE_DGRAM)
108                       continue;
109                     else
110                       clnt_saddr.sin_addr.s_addr =
111                         inetstr2int (server->ep.ep_val[i].uaddr);
112                   }
113           }
114         else
115           continue;
116
117       clnt_sock = RPC_ANYSOCK;
118       if ((flags & USE_DGRAM) == USE_DGRAM)
119         client = clntudp_create (&clnt_saddr, NIS_PROG, NIS_VERSION,
120                                  TIMEOUT, &clnt_sock);
121       else
122         client = clnttcp_create (&clnt_saddr, NIS_PROG, NIS_VERSION,
123                                  &clnt_sock, 0, 0);
124
125       if (client == NULL)
126         continue;
127       if (clnt_call (client, 0, (xdrproc_t) xdr_void, NULL,
128                      (xdrproc_t) xdr_void, NULL, TIMEOUT) != RPC_SUCCESS)
129         {
130           clnt_destroy (client);
131           continue;
132         }
133
134       if ((flags & NO_AUTHINFO) != NO_AUTHINFO)
135         {
136 #if defined(HAVE_SECURE_RPC)
137           if (server->key_type == NIS_PK_DH && getenv ("NO_SECURE_RPC") == NULL)
138             {
139               char netname[MAXNETNAMELEN+1];
140               char *p;
141
142               p = stpcpy (netname, "unix.");
143               strncpy (p, server->name,MAXNETNAMELEN-5);
144               netname[MAXNETNAMELEN] = '\0';
145               p = strchr (netname, '.');
146               *p = '@';
147               client->cl_auth =
148                 authdes_pk_create (netname, &server->pkey, 300, NULL, NULL);
149               if (!client->cl_auth)
150                 client->cl_auth = authunix_create_default ();
151             }
152           else
153 #endif
154             client->cl_auth = authunix_create_default ();
155         }
156       return client;
157     }
158
159   return NULL;
160 }
161
162 nis_error
163 __do_niscall2 (const nis_server *server, u_int server_len, u_long prog,
164                xdrproc_t xargs, caddr_t req, xdrproc_t xres, caddr_t resp,
165                u_long flags)
166 {
167   CLIENT *clnt;
168   int try, result;
169
170   try = 0;
171   result = NIS_NAMEUNREACHABLE;
172
173   if (((flags & MASTER_ONLY) == MASTER_ONLY) && server_len > 1)
174     server_len = 1; /* The first entry is the master */
175
176   while (try < MAXTRIES && result != RPC_SUCCESS)
177     {
178       unsigned int i;
179
180       if ((flags & HARD_LOOKUP) == 0)
181         ++try;
182
183       for (i = 0; i < server_len; i++)
184         {
185           if ((clnt = __nis_dobind (&server[i], flags)) == NULL)
186             continue;
187
188           result = clnt_call (clnt, prog, xargs, req, xres, resp, TIMEOUT);
189
190           if (result != RPC_SUCCESS)
191             {
192               clnt_perror (clnt, "do_niscall: clnt_call");
193               clnt_destroy (clnt);
194               result = NIS_RPCERROR;
195             }
196           else
197             clnt_destroy (clnt);
198         }
199     }
200
201   return result;
202 }
203
204 static directory_obj *
205 dir_lookup (const_nis_name name, nis_server *serv, u_long flags)
206 {
207   CLIENT *clnt;
208   int try, result;
209   nis_result *res;
210   struct ns_request req;
211   directory_obj *dir;
212
213   res = calloc (1, sizeof (nis_result));
214   req.ns_name = (char *)name;
215   req.ns_object.ns_object_len = 0;
216   req.ns_object.ns_object_val = NULL;
217   try = 0;
218   result = NIS_NAMEUNREACHABLE;
219
220   while (try < MAXTRIES && result != RPC_SUCCESS)
221     {
222       if ((clnt = __nis_dobind (serv, flags)) == NULL)
223         continue;
224
225       result = clnt_call (clnt, NIS_LOOKUP, (xdrproc_t) xdr_ns_request,
226                           (caddr_t) &req, (xdrproc_t) xdr_nis_result,
227                           (caddr_t) res, TIMEOUT);
228
229       if (result != RPC_SUCCESS)
230         {
231           clnt_perror (clnt, "do_niscall: clnt_call");
232           clnt_destroy (clnt);
233           result = NIS_RPCERROR;
234         }
235       else
236         clnt_destroy (clnt);
237     }
238   if (result != RPC_SUCCESS || res->status != NIS_SUCCESS)
239     return NULL;
240
241   dir = nis_clone_directory (&res->objects.objects_val->DI_data, NULL);
242   nis_freeresult (res);
243
244   return dir;
245 }
246
247 static directory_obj *
248 rec_dirsearch (const_nis_name name, directory_obj *dir, u_long flags)
249 {
250   char domain [strlen (name) + 3];
251
252   nis_domain_of_r (name, domain, sizeof (domain));
253   if (strncmp (domain, "org_dir.", 8) == 0)
254     {
255       char tmp[strlen (name) + 3];
256
257       nis_domain_of_r (domain, tmp, sizeof (tmp));
258       strcpy (domain, tmp);
259     }
260   else
261     if (strncmp (domain, "groups_dir.", 11) == 0)
262       {
263         char tmp[strlen (name) + 3];
264
265         nis_domain_of_r (domain, tmp, sizeof (tmp));
266         strcpy (domain, tmp);
267       }
268     else
269       {
270         /* We have no grous_dir or org_dir, so try the complete name */
271         strcpy (domain, name);
272       }
273
274   switch (nis_dir_cmp (domain, dir->do_name))
275     {
276     case SAME_NAME:
277       return dir;
278     case NOT_SEQUENTIAL:
279       /* NOT_SEQUENTIAL means, go one up and try it there ! */
280     case HIGHER_NAME:
281       { /* We need data from a parent domain */
282         directory_obj *obj;
283         char ndomain [strlen (name) + 3];
284
285         nis_domain_of_r (dir->do_name, ndomain, sizeof (ndomain));
286
287         /* The root server of our domain is a replica of the parent
288            domain ! (Now I understand why a root server must be a
289            replica of the parent domain) */
290         obj = dir_lookup (ndomain, dir->do_servers.do_servers_val,
291                           flags);
292         if (obj != NULL)
293           {
294             /* We have found a NIS+ server serving ndomain, now
295                let us search for "name" */
296             nis_free_directory (dir);
297             return rec_dirsearch (name, obj, flags);
298           }
299         else
300           {
301             /* Ups, very bad. Are we already the root server ? */
302             nis_free_directory (dir);
303             return NULL;
304           }
305       }
306       break;
307     case LOWER_NAME:
308       {
309         directory_obj *obj;
310         char leaf [strlen (name) + 3];
311         char ndomain [strlen (name) + 3];
312         u_int i;
313         char *cp;
314
315         do
316           {
317             if (strlen (domain) == 0)
318               {
319                 nis_free_directory (dir);
320                 return NULL;
321               }
322             nis_leaf_of_r (domain, leaf, sizeof (leaf));
323             nis_domain_of_r (domain, ndomain, sizeof (ndomain));
324             strcpy (domain, ndomain);
325           }
326         while (nis_dir_cmp (domain, dir->do_name) != SAME_NAME);
327         cp = strchr (leaf, '\0');
328         *cp++ = '.';
329         strcpy (cp, domain);
330
331         for (i = 0; i < dir->do_servers.do_servers_len; ++i)
332           {
333             obj = dir_lookup (leaf, &dir->do_servers.do_servers_val[i],
334                               flags);
335             if (obj != NULL)
336               {
337                 /* We have found a NIS+ server serving ndomain, now
338                    let us search for "name" */
339                 nis_free_directory (dir);
340                 return rec_dirsearch (name, obj, flags);
341               }
342           }
343       }
344       break;
345     case BAD_NAME:
346       nis_free_directory (dir);
347       return NULL;
348     }
349   nis_free_directory (dir);
350   return NULL;
351 }
352
353 nis_error
354 __do_niscall (const_nis_name name, u_long prog, xdrproc_t xargs,
355               caddr_t req, xdrproc_t xres, caddr_t resp, u_long flags)
356 {
357   nis_error result;
358   directory_obj *dir = NULL;
359   const nis_server *server;
360   u_int server_len;
361
362
363   dir = readColdStartFile ();
364   if (dir == NULL) /* No /var/nis/NIS_COLD_START -> no NIS+ installed */
365     return NIS_UNAVAIL;
366
367   if (name != NULL)
368     {
369       dir = rec_dirsearch (name, dir, flags);
370       if (dir == NULL)
371         {
372           if (nis_dir_cmp (nis_local_directory(), name) == NOT_SEQUENTIAL)
373             return NIS_NAMEUNREACHABLE;
374           else
375             return NIS_NOTFOUND;
376         }
377     }
378   server = dir->do_servers.do_servers_val;
379   server_len = dir->do_servers.do_servers_len;
380
381   if (((flags & MASTER_ONLY) == MASTER_ONLY) && server_len > 1)
382     server_len = 1; /* The first entry is the master */
383
384   result = __do_niscall2 (server, server_len, prog, xargs, req, xres,
385                           resp, flags);
386   if (dir != NULL)
387     nis_free_directory (dir);
388
389   return result;
390 }