(getspnam_plususer): Preserve original return value.
[kopensolaris-gnu/glibc.git] / nis / ypclnt.c
1 /* Copyright (C) 1996-2001, 2002, 2003, 2004, 2005
2    Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Thorsten Kukuk <kukuk@suse.de>, 1996.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, write to the Free
18    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <libintl.h>
26 #include <rpc/rpc.h>
27 #include <rpcsvc/nis.h>
28 #include <rpcsvc/yp.h>
29 #include <rpcsvc/ypclnt.h>
30 #include <rpcsvc/ypupd.h>
31 #include <sys/uio.h>
32 #include <bits/libc-lock.h>
33
34 /* This should only be defined on systems with a BSD compatible ypbind */
35 #ifndef BINDINGDIR
36 # define BINDINGDIR "/var/yp/binding"
37 #endif
38
39 struct dom_binding
40   {
41     struct dom_binding *dom_pnext;
42     char dom_domain[YPMAXDOMAIN + 1];
43     struct sockaddr_in dom_server_addr;
44     int dom_socket;
45     CLIENT *dom_client;
46   };
47 typedef struct dom_binding dom_binding;
48
49 static struct timeval RPCTIMEOUT = {25, 0};
50 static struct timeval UDPTIMEOUT = {5, 0};
51 static int const MAXTRIES = 2;
52 static char __ypdomainname[NIS_MAXNAMELEN + 1] = "\0";
53 __libc_lock_define_initialized (static, ypbindlist_lock)
54 static dom_binding *__ypbindlist = NULL;
55
56
57 static void
58 yp_bind_client_create (const char *domain, dom_binding *ysd,
59                        struct ypbind_resp *ypbr)
60 {
61   ysd->dom_server_addr.sin_family = AF_INET;
62   memcpy (&ysd->dom_server_addr.sin_port,
63           ypbr->ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port,
64           sizeof (ysd->dom_server_addr.sin_port));
65   memcpy (&ysd->dom_server_addr.sin_addr.s_addr,
66           ypbr->ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr,
67           sizeof (ysd->dom_server_addr.sin_addr.s_addr));
68   strncpy (ysd->dom_domain, domain, YPMAXDOMAIN);
69   ysd->dom_domain[YPMAXDOMAIN] = '\0';
70
71   ysd->dom_socket = RPC_ANYSOCK;
72   ysd->dom_client = clntudp_create (&ysd->dom_server_addr, YPPROG, YPVERS,
73                                     UDPTIMEOUT, &ysd->dom_socket);
74
75   if (ysd->dom_client != NULL)
76     {
77       /* If the program exits, close the socket */
78       if (fcntl (ysd->dom_socket, F_SETFD, FD_CLOEXEC) == -1)
79         perror ("fcntl: F_SETFD");
80     }
81 }
82
83 #if USE_BINDINGDIR
84 static void
85 yp_bind_file (const char *domain, dom_binding *ysd)
86 {
87   char path[sizeof (BINDINGDIR) + strlen (domain) + 3 * sizeof (unsigned) + 3];
88
89   snprintf (path, sizeof (path), "%s/%s.%u", BINDINGDIR, domain, YPBINDVERS);
90   int fd = open (path, O_RDONLY);
91   if (fd >= 0)
92     {
93       /* We have a binding file and could save a RPC call.  The file
94          contains a port number and the YPBIND_RESP record.  The port
95          number (16 bits) can be ignored.  */
96       struct ypbind_resp ypbr;
97
98       if (pread (fd, &ypbr, sizeof (ypbr), 2) == sizeof (ypbr))
99         yp_bind_client_create (domain, ysd, &ypbr);
100
101       close (fd);
102     }
103 }
104 #endif
105
106 static int
107 yp_bind_ypbindprog (const char *domain, dom_binding *ysd)
108 {
109   struct sockaddr_in clnt_saddr;
110   struct ypbind_resp ypbr;
111   int clnt_sock;
112   CLIENT *client;
113
114   memset (&clnt_saddr, '\0', sizeof clnt_saddr);
115   clnt_saddr.sin_family = AF_INET;
116   clnt_saddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
117   clnt_sock = RPC_ANYSOCK;
118   client = clnttcp_create (&clnt_saddr, YPBINDPROG, YPBINDVERS,
119                            &clnt_sock, 0, 0);
120   if (client == NULL)
121     return YPERR_YPBIND;
122
123   /* Check the port number -- should be < IPPORT_RESERVED.
124      If not, it's possible someone has registered a bogus
125      ypbind with the portmapper and is trying to trick us. */
126   if (ntohs (clnt_saddr.sin_port) >= IPPORT_RESERVED)
127     {
128       clnt_destroy (client);
129       return YPERR_YPBIND;
130     }
131
132   if (clnt_call (client, YPBINDPROC_DOMAIN,
133                  (xdrproc_t) xdr_domainname, (caddr_t) &domain,
134                  (xdrproc_t) xdr_ypbind_resp,
135                  (caddr_t) &ypbr, RPCTIMEOUT) != RPC_SUCCESS)
136     {
137       clnt_destroy (client);
138       return YPERR_YPBIND;
139     }
140
141   clnt_destroy (client);
142
143   if (ypbr.ypbind_status != YPBIND_SUCC_VAL)
144     {
145       fprintf (stderr, _("YPBINDPROC_DOMAIN: %s\n"),
146                ypbinderr_string (ypbr.ypbind_resp_u.ypbind_error));
147       return YPERR_DOMAIN;
148     }
149   memset (&ysd->dom_server_addr, '\0', sizeof ysd->dom_server_addr);
150
151   yp_bind_client_create (domain, ysd, &ypbr);
152
153   return YPERR_SUCCESS;
154 }
155
156 static int
157 __yp_bind (const char *domain, dom_binding **ypdb)
158 {
159   dom_binding *ysd = NULL;
160   int is_new = 0;
161
162   if (domain == NULL || domain[0] == '\0')
163     return YPERR_BADARGS;
164
165   ysd = *ypdb;
166   while (ysd != NULL)
167     {
168       if (strcmp (domain, ysd->dom_domain) == 0)
169         break;
170       ysd = ysd->dom_pnext;
171     }
172
173   if (ysd == NULL)
174     {
175       is_new = 1;
176       ysd = (dom_binding *) calloc (1, sizeof *ysd);
177       if (__builtin_expect (ysd == NULL, 0))
178         return YPERR_RESRC;
179     }
180
181 #if USE_BINDINGDIR
182   /* Try binding dir at first if we have no binding */
183   if (ysd->dom_client == NULL)
184     yp_bind_file (domain, ysd);
185 #endif /* USE_BINDINGDIR */
186
187   if (ysd->dom_client == NULL)
188     {
189       int retval = yp_bind_ypbindprog (domain, ysd);
190       if (retval != YPERR_SUCCESS)
191         {
192           if (is_new)
193             free (ysd);
194           return retval;
195         }
196     }
197
198   if (ysd->dom_client == NULL)
199     {
200       if (is_new)
201         free (ysd);
202       return YPERR_YPSERV;
203     }
204
205   if (is_new)
206     {
207       ysd->dom_pnext = *ypdb;
208       *ypdb = ysd;
209     }
210
211   return YPERR_SUCCESS;
212 }
213
214 static void
215 __yp_unbind (dom_binding *ydb)
216 {
217   clnt_destroy (ydb->dom_client);
218   free (ydb);
219 }
220
221 int
222 yp_bind (const char *indomain)
223 {
224   int status;
225
226   __libc_lock_lock (ypbindlist_lock);
227
228   status = __yp_bind (indomain, &__ypbindlist);
229
230   __libc_lock_unlock (ypbindlist_lock);
231
232   return status;
233 }
234 libnsl_hidden_def (yp_bind)
235
236 static void
237 yp_unbind_locked (const char *indomain)
238 {
239   dom_binding *ydbptr, *ydbptr2;
240
241   ydbptr2 = NULL;
242   ydbptr = __ypbindlist;
243
244   while (ydbptr != NULL)
245     {
246       if (strcmp (ydbptr->dom_domain, indomain) == 0)
247         {
248           dom_binding *work;
249
250           work = ydbptr;
251           if (ydbptr2 == NULL)
252             __ypbindlist = __ypbindlist->dom_pnext;
253           else
254             ydbptr2 = ydbptr->dom_pnext;
255           __yp_unbind (work);
256           break;
257         }
258       ydbptr2 = ydbptr;
259       ydbptr = ydbptr->dom_pnext;
260     }
261 }
262
263 void
264 yp_unbind (const char *indomain)
265 {
266   __libc_lock_lock (ypbindlist_lock);
267
268   yp_unbind_locked (indomain);
269
270   __libc_lock_unlock (ypbindlist_lock);
271
272   return;
273 }
274
275 static int
276 __ypclnt_call (const char *domain, u_long prog, xdrproc_t xargs,
277                caddr_t req, xdrproc_t xres, caddr_t resp, dom_binding **ydb,
278                int print_error)
279 {
280   enum clnt_stat result;
281
282   result = clnt_call ((*ydb)->dom_client, prog,
283                       xargs, req, xres, resp, RPCTIMEOUT);
284
285   if (result != RPC_SUCCESS)
286     {
287       /* We don't print an error message, if we try our old,
288          cached data. Only print this for data, which should work.  */
289       if (print_error)
290         clnt_perror ((*ydb)->dom_client, "do_ypcall: clnt_call");
291
292       return YPERR_RPC;
293     }
294
295   return YPERR_SUCCESS;
296 }
297
298 static int
299 do_ypcall (const char *domain, u_long prog, xdrproc_t xargs,
300            caddr_t req, xdrproc_t xres, caddr_t resp)
301 {
302   dom_binding *ydb;
303   int status;
304   int saved_errno = errno;
305
306   status = YPERR_YPERR;
307
308   __libc_lock_lock (ypbindlist_lock);
309   ydb = __ypbindlist;
310   while (ydb != NULL)
311     {
312       if (strcmp (domain, ydb->dom_domain) == 0)
313         {
314           if (__yp_bind (domain, &ydb) == 0)
315             {
316               /* Call server, print no error message, do not unbind.  */
317               status = __ypclnt_call (domain, prog, xargs, req, xres,
318                                       resp, &ydb, 0);
319               if (status == YPERR_SUCCESS)
320                 {
321                   __libc_lock_unlock (ypbindlist_lock);
322                   __set_errno (saved_errno);
323                   return status;
324                 }
325             }
326           /* We use ypbindlist, and the old cached data is
327              invalid. unbind now and create a new binding */
328           yp_unbind_locked (domain);
329
330           break;
331         }
332       ydb = ydb->dom_pnext;
333     }
334   __libc_lock_unlock (ypbindlist_lock);
335
336   /* First try with cached data failed. Now try to get
337      current data from the system.  */
338   ydb = NULL;
339   if (__yp_bind (domain, &ydb) == 0)
340     {
341       status = __ypclnt_call (domain, prog, xargs, req, xres,
342                               resp, &ydb, 1);
343       __yp_unbind (ydb);
344     }
345
346 #if USE_BINDINGDIR
347   /* If we support binding dir data, we have a third chance:
348      Ask ypbind.  */
349   if (status != YPERR_SUCCESS)
350     {
351       ydb = calloc (1, sizeof (dom_binding));
352       if (yp_bind_ypbindprog (domain, ydb) == YPERR_SUCCESS)
353         {
354           status = __ypclnt_call (domain, prog, xargs, req, xres,
355                                   resp, &ydb, 1);
356           __yp_unbind (ydb);
357         }
358       else
359         free (ydb);
360     }
361 #endif
362
363   __set_errno (saved_errno);
364
365   return status;
366 }
367
368
369 __libc_lock_define_initialized (static, domainname_lock)
370
371 int
372 yp_get_default_domain (char **outdomain)
373 {
374   int result = YPERR_SUCCESS;;
375   *outdomain = NULL;
376
377   __libc_lock_lock (domainname_lock);
378
379   if (__ypdomainname[0] == '\0')
380     {
381       if (getdomainname (__ypdomainname, NIS_MAXNAMELEN))
382         result = YPERR_NODOM;
383       else if (strcmp (__ypdomainname, "(none)") == 0)
384         {
385           /* If domainname is not set, some systems will return "(none)" */
386           __ypdomainname[0] = '\0';
387           result = YPERR_NODOM;
388         }
389       else
390         *outdomain = __ypdomainname;
391     }
392   else
393     *outdomain = __ypdomainname;
394
395   __libc_lock_unlock (domainname_lock);
396
397   return result;
398 }
399 libnsl_hidden_def (yp_get_default_domain)
400
401 int
402 __yp_check (char **domain)
403 {
404   char *unused;
405
406   if (__ypdomainname[0] == '\0')
407     if (yp_get_default_domain (&unused))
408       return 0;
409
410   if (domain)
411     *domain = __ypdomainname;
412
413   if (yp_bind (__ypdomainname) == 0)
414     return 1;
415   return 0;
416 }
417
418 int
419 yp_match (const char *indomain, const char *inmap, const char *inkey,
420           const int inkeylen, char **outval, int *outvallen)
421 {
422   ypreq_key req;
423   ypresp_val resp;
424   enum clnt_stat result;
425
426   if (indomain == NULL || indomain[0] == '\0' ||
427       inmap == NULL || inmap[0] == '\0' ||
428       inkey == NULL || inkey[0] == '\0' || inkeylen <= 0)
429     return YPERR_BADARGS;
430
431   req.domain = (char *) indomain;
432   req.map = (char *) inmap;
433   req.key.keydat_val = (char *) inkey;
434   req.key.keydat_len = inkeylen;
435
436   *outval = NULL;
437   *outvallen = 0;
438   memset (&resp, '\0', sizeof (resp));
439
440   result = do_ypcall (indomain, YPPROC_MATCH, (xdrproc_t) xdr_ypreq_key,
441                       (caddr_t) & req, (xdrproc_t) xdr_ypresp_val,
442                       (caddr_t) & resp);
443
444   if (result != YPERR_SUCCESS)
445     return result;
446   if (resp.stat != YP_TRUE)
447     return ypprot_err (resp.stat);
448
449   *outvallen = resp.val.valdat_len;
450   *outval = malloc (*outvallen + 1);
451   if (__builtin_expect (*outval == NULL, 0))
452     return YPERR_RESRC;
453   memcpy (*outval, resp.val.valdat_val, *outvallen);
454   (*outval)[*outvallen] = '\0';
455
456   xdr_free ((xdrproc_t) xdr_ypresp_val, (char *) &resp);
457
458   return YPERR_SUCCESS;
459 }
460
461 int
462 yp_first (const char *indomain, const char *inmap, char **outkey,
463           int *outkeylen, char **outval, int *outvallen)
464 {
465   ypreq_nokey req;
466   ypresp_key_val resp;
467   enum clnt_stat result;
468
469   if (indomain == NULL || indomain[0] == '\0' ||
470       inmap == NULL || inmap[0] == '\0')
471     return YPERR_BADARGS;
472
473   req.domain = (char *) indomain;
474   req.map = (char *) inmap;
475
476   *outkey = *outval = NULL;
477   *outkeylen = *outvallen = 0;
478   memset (&resp, '\0', sizeof (resp));
479
480   result = do_ypcall (indomain, YPPROC_FIRST, (xdrproc_t) xdr_ypreq_nokey,
481                       (caddr_t) & req, (xdrproc_t) xdr_ypresp_key_val,
482                       (caddr_t) & resp);
483
484   if (result != RPC_SUCCESS)
485     return YPERR_RPC;
486   if (resp.stat != YP_TRUE)
487     return ypprot_err (resp.stat);
488
489   *outkeylen = resp.key.keydat_len;
490   *outkey = malloc (*outkeylen + 1);
491   if (__builtin_expect (*outkey == NULL, 0))
492     return YPERR_RESRC;
493   memcpy (*outkey, resp.key.keydat_val, *outkeylen);
494   (*outkey)[*outkeylen] = '\0';
495   *outvallen = resp.val.valdat_len;
496   *outval = malloc (*outvallen + 1);
497   if (__builtin_expect (*outval == NULL, 0))
498     return YPERR_RESRC;
499   memcpy (*outval, resp.val.valdat_val, *outvallen);
500   (*outval)[*outvallen] = '\0';
501
502   xdr_free ((xdrproc_t) xdr_ypresp_key_val, (char *) &resp);
503
504   return YPERR_SUCCESS;
505 }
506
507 int
508 yp_next (const char *indomain, const char *inmap, const char *inkey,
509          const int inkeylen, char **outkey, int *outkeylen, char **outval,
510          int *outvallen)
511 {
512   ypreq_key req;
513   ypresp_key_val resp;
514   enum clnt_stat result;
515
516   if (indomain == NULL || indomain[0] == '\0' ||
517       inmap == NULL || inmap[0] == '\0' ||
518       inkeylen <= 0 || inkey == NULL || inkey[0] == '\0')
519     return YPERR_BADARGS;
520
521   req.domain = (char *) indomain;
522   req.map = (char *) inmap;
523   req.key.keydat_val = (char *) inkey;
524   req.key.keydat_len = inkeylen;
525
526   *outkey = *outval = NULL;
527   *outkeylen = *outvallen = 0;
528   memset (&resp, '\0', sizeof (resp));
529
530   result = do_ypcall (indomain, YPPROC_NEXT, (xdrproc_t) xdr_ypreq_key,
531                       (caddr_t) & req, (xdrproc_t) xdr_ypresp_key_val,
532                       (caddr_t) & resp);
533
534   if (result != YPERR_SUCCESS)
535     return result;
536   if (resp.stat != YP_TRUE)
537     return ypprot_err (resp.stat);
538
539   *outkeylen = resp.key.keydat_len;
540   *outkey = malloc (*outkeylen + 1);
541   if (__builtin_expect (*outkey == NULL, 0))
542     return YPERR_RESRC;
543   memcpy (*outkey, resp.key.keydat_val, *outkeylen);
544   (*outkey)[*outkeylen] = '\0';
545   *outvallen = resp.val.valdat_len;
546   *outval = malloc (*outvallen + 1);
547   if (__builtin_expect (*outval == NULL, 0))
548     return YPERR_RESRC;
549   memcpy (*outval, resp.val.valdat_val, *outvallen);
550   (*outval)[*outvallen] = '\0';
551
552   xdr_free ((xdrproc_t) xdr_ypresp_key_val, (char *) &resp);
553
554   return YPERR_SUCCESS;
555 }
556
557 int
558 yp_master (const char *indomain, const char *inmap, char **outname)
559 {
560   ypreq_nokey req;
561   ypresp_master resp;
562   enum clnt_stat result;
563
564   if (indomain == NULL || indomain[0] == '\0' ||
565       inmap == NULL || inmap[0] == '\0')
566     return YPERR_BADARGS;
567
568   req.domain = (char *) indomain;
569   req.map = (char *) inmap;
570
571   memset (&resp, '\0', sizeof (ypresp_master));
572
573   result = do_ypcall (indomain, YPPROC_MASTER, (xdrproc_t) xdr_ypreq_nokey,
574           (caddr_t) & req, (xdrproc_t) xdr_ypresp_master, (caddr_t) & resp);
575
576   if (result != YPERR_SUCCESS)
577     return result;
578   if (resp.stat != YP_TRUE)
579     return ypprot_err (resp.stat);
580
581   *outname = strdup (resp.peer);
582   xdr_free ((xdrproc_t) xdr_ypresp_master, (char *) &resp);
583
584   return *outname == NULL ? YPERR_YPERR : YPERR_SUCCESS;
585 }
586 libnsl_hidden_def (yp_master)
587
588 int
589 yp_order (const char *indomain, const char *inmap, unsigned int *outorder)
590 {
591   struct ypreq_nokey req;
592   struct ypresp_order resp;
593   enum clnt_stat result;
594
595   if (indomain == NULL || indomain[0] == '\0' ||
596       inmap == NULL || inmap == '\0')
597     return YPERR_BADARGS;
598
599   req.domain = (char *) indomain;
600   req.map = (char *) inmap;
601
602   memset (&resp, '\0', sizeof (resp));
603
604   result = do_ypcall (indomain, YPPROC_ORDER, (xdrproc_t) xdr_ypreq_nokey,
605            (caddr_t) & req, (xdrproc_t) xdr_ypresp_order, (caddr_t) & resp);
606
607   if (result != YPERR_SUCCESS)
608     return result;
609   if (resp.stat != YP_TRUE)
610     return ypprot_err (resp.stat);
611
612   *outorder = resp.ordernum;
613   xdr_free ((xdrproc_t) xdr_ypresp_order, (char *) &resp);
614
615   return YPERR_SUCCESS;
616 }
617
618 struct ypresp_all_data
619 {
620   unsigned long status;
621   void *data;
622   int (*foreach) (int status, char *key, int keylen,
623                   char *val, int vallen, char *data);
624 };
625
626 static bool_t
627 __xdr_ypresp_all (XDR *xdrs, struct ypresp_all_data *objp)
628 {
629   while (1)
630     {
631       struct ypresp_all resp;
632
633       memset (&resp, '\0', sizeof (struct ypresp_all));
634       if (!xdr_ypresp_all (xdrs, &resp))
635         {
636           xdr_free ((xdrproc_t) xdr_ypresp_all, (char *) &resp);
637           objp->status = YP_YPERR;
638           return FALSE;
639         }
640       if (resp.more == 0)
641         {
642           xdr_free ((xdrproc_t) xdr_ypresp_all, (char *) &resp);
643           objp->status = YP_NOMORE;
644           return TRUE;
645         }
646
647       switch (resp.ypresp_all_u.val.stat)
648         {
649         case YP_TRUE:
650           {
651             char key[resp.ypresp_all_u.val.key.keydat_len + 1];
652             char val[resp.ypresp_all_u.val.val.valdat_len + 1];
653             int keylen = resp.ypresp_all_u.val.key.keydat_len;
654             int vallen = resp.ypresp_all_u.val.val.valdat_len;
655
656             /* We are not allowed to modify the key and val data.
657                But we are allowed to add data behind the buffer,
658                if we don't modify the length. So add an extra NUL
659                character to avoid trouble with broken code. */
660             objp->status = YP_TRUE;
661             memcpy (key, resp.ypresp_all_u.val.key.keydat_val, keylen);
662             key[keylen] = '\0';
663             memcpy (val, resp.ypresp_all_u.val.val.valdat_val, vallen);
664             val[vallen] = '\0';
665             xdr_free ((xdrproc_t) xdr_ypresp_all, (char *) &resp);
666             if ((*objp->foreach) (objp->status, key, keylen,
667                                   val, vallen, objp->data))
668               return TRUE;
669           }
670           break;
671         default:
672           objp->status = resp.ypresp_all_u.val.stat;
673           xdr_free ((xdrproc_t) xdr_ypresp_all, (char *) &resp);
674           /* Sun says we don't need to make this call, but must return
675              immediatly. Since Solaris makes this call, we will call
676              the callback function, too. */
677           (*objp->foreach) (objp->status, NULL, 0, NULL, 0, objp->data);
678           return TRUE;
679         }
680     }
681 }
682
683 int
684 yp_all (const char *indomain, const char *inmap,
685         const struct ypall_callback *incallback)
686 {
687   struct ypreq_nokey req;
688   dom_binding *ydb = NULL;
689   int try, res;
690   enum clnt_stat result;
691   struct sockaddr_in clnt_sin;
692   CLIENT *clnt;
693   struct ypresp_all_data data;
694   int clnt_sock;
695   int saved_errno = errno;
696
697   if (indomain == NULL || indomain[0] == '\0' ||
698       inmap == NULL || inmap == '\0')
699     return YPERR_BADARGS;
700
701   try = 0;
702   res = YPERR_YPERR;
703
704   while (try < MAXTRIES && res != YPERR_SUCCESS)
705     {
706       if (__yp_bind (indomain, &ydb) != 0)
707         {
708           __set_errno (saved_errno);
709           return YPERR_DOMAIN;
710         }
711
712       clnt_sock = RPC_ANYSOCK;
713       clnt_sin = ydb->dom_server_addr;
714       clnt_sin.sin_port = 0;
715
716       /* We don't need the UDP connection anymore.  */
717       __yp_unbind (ydb);
718       ydb = NULL;
719
720       clnt = clnttcp_create (&clnt_sin, YPPROG, YPVERS, &clnt_sock, 0, 0);
721       if (clnt == NULL)
722         {
723           __set_errno (saved_errno);
724           return YPERR_PMAP;
725         }
726       req.domain = (char *) indomain;
727       req.map = (char *) inmap;
728
729       data.foreach = incallback->foreach;
730       data.data = (void *) incallback->data;
731
732       result = clnt_call (clnt, YPPROC_ALL, (xdrproc_t) xdr_ypreq_nokey,
733                           (caddr_t) &req, (xdrproc_t) __xdr_ypresp_all,
734                           (caddr_t) &data, RPCTIMEOUT);
735
736       if (result != RPC_SUCCESS)
737         {
738           /* Print the error message only on the last try */
739           if (try == MAXTRIES - 1)
740             clnt_perror (clnt, "yp_all: clnt_call");
741           res = YPERR_RPC;
742         }
743       else
744         res = YPERR_SUCCESS;
745
746       clnt_destroy (clnt);
747
748       if (res == YPERR_SUCCESS && data.status != YP_NOMORE)
749         {
750           __set_errno (saved_errno);
751           return ypprot_err (data.status);
752         }
753       ++try;
754     }
755
756   __set_errno (saved_errno);
757
758   return res;
759 }
760
761 int
762 yp_maplist (const char *indomain, struct ypmaplist **outmaplist)
763 {
764   struct ypresp_maplist resp;
765   enum clnt_stat result;
766
767   if (indomain == NULL || indomain[0] == '\0')
768     return YPERR_BADARGS;
769
770   memset (&resp, '\0', sizeof (resp));
771
772   result = do_ypcall (indomain, YPPROC_MAPLIST, (xdrproc_t) xdr_domainname,
773     (caddr_t) & indomain, (xdrproc_t) xdr_ypresp_maplist, (caddr_t) & resp);
774
775   if (result != YPERR_SUCCESS)
776     return result;
777   if (resp.stat != YP_TRUE)
778     return ypprot_err (resp.stat);
779
780   *outmaplist = resp.maps;
781   /* We give the list not free, this will be done by ypserv
782      xdr_free((xdrproc_t)xdr_ypresp_maplist, (char *)&resp); */
783
784   return YPERR_SUCCESS;
785 }
786
787 const char *
788 yperr_string (const int error)
789 {
790   switch (error)
791     {
792     case YPERR_SUCCESS:
793       return _("Success");
794     case YPERR_BADARGS:
795       return _("Request arguments bad");
796     case YPERR_RPC:
797       return _("RPC failure on NIS operation");
798     case YPERR_DOMAIN:
799       return _("Can't bind to server which serves this domain");
800     case YPERR_MAP:
801       return _("No such map in server's domain");
802     case YPERR_KEY:
803       return _("No such key in map");
804     case YPERR_YPERR:
805       return _("Internal NIS error");
806     case YPERR_RESRC:
807       return _("Local resource allocation failure");
808     case YPERR_NOMORE:
809       return _("No more records in map database");
810     case YPERR_PMAP:
811       return _("Can't communicate with portmapper");
812     case YPERR_YPBIND:
813       return _("Can't communicate with ypbind");
814     case YPERR_YPSERV:
815       return _("Can't communicate with ypserv");
816     case YPERR_NODOM:
817       return _("Local domain name not set");
818     case YPERR_BADDB:
819       return _("NIS map database is bad");
820     case YPERR_VERS:
821       return _("NIS client/server version mismatch - can't supply service");
822     case YPERR_ACCESS:
823       return _("Permission denied");
824     case YPERR_BUSY:
825       return _("Database is busy");
826     }
827   return _("Unknown NIS error code");
828 }
829
830 static const int8_t yp_2_yperr[] =
831   {
832 #define YP2YPERR(yp, yperr)  [YP_##yp - YP_VERS] = YPERR_##yperr
833     YP2YPERR (TRUE, SUCCESS),
834     YP2YPERR (NOMORE, NOMORE),
835     YP2YPERR (FALSE, YPERR),
836     YP2YPERR (NOMAP, MAP),
837     YP2YPERR (NODOM, DOMAIN),
838     YP2YPERR (NOKEY, KEY),
839     YP2YPERR (BADOP, YPERR),
840     YP2YPERR (BADDB, BADDB),
841     YP2YPERR (YPERR, YPERR),
842     YP2YPERR (BADARGS, BADARGS),
843     YP2YPERR (VERS, VERS)
844   };
845 int
846 ypprot_err (const int code)
847 {
848   if (code < YP_VERS || code > YP_NOMORE)
849     return YPERR_YPERR;
850   return yp_2_yperr[code - YP_VERS];
851 }
852 libnsl_hidden_def (ypprot_err)
853
854 const char *
855 ypbinderr_string (const int error)
856 {
857   switch (error)
858     {
859     case 0:
860       return _("Success");
861     case YPBIND_ERR_ERR:
862       return _("Internal ypbind error");
863     case YPBIND_ERR_NOSERV:
864       return _("Domain not bound");
865     case YPBIND_ERR_RESC:
866       return _("System resource allocation failure");
867     default:
868       return _("Unknown ypbind error");
869     }
870 }
871 libnsl_hidden_def (ypbinderr_string)
872
873 #define WINDOW 60
874
875 int
876 yp_update (char *domain, char *map, unsigned ypop,
877            char *key, int keylen, char *data, int datalen)
878 {
879   union
880     {
881       ypupdate_args update_args;
882       ypdelete_args delete_args;
883     }
884   args;
885   xdrproc_t xdr_argument;
886   unsigned res = 0;
887   CLIENT *clnt;
888   char *master;
889   struct sockaddr saddr;
890   char servername[MAXNETNAMELEN + 1];
891   int r;
892
893   if (!domain || !map || !key || (ypop != YPOP_DELETE && !data))
894     return YPERR_BADARGS;
895
896   args.update_args.mapname = map;
897   args.update_args.key.yp_buf_len = keylen;
898   args.update_args.key.yp_buf_val = key;
899   args.update_args.datum.yp_buf_len = datalen;
900   args.update_args.datum.yp_buf_val = data;
901
902   if ((r = yp_master (domain, map, &master)) != 0)
903     return r;
904
905   if (!host2netname (servername, master, domain))
906     {
907       fputs (_("yp_update: cannot convert host to netname\n"), stderr);
908       return YPERR_YPERR;
909     }
910
911   if ((clnt = clnt_create (master, YPU_PROG, YPU_VERS, "tcp")) == NULL)
912     {
913       clnt_pcreateerror ("yp_update: clnt_create");
914       return YPERR_RPC;
915     }
916
917   if (!clnt_control (clnt, CLGET_SERVER_ADDR, (char *) &saddr))
918     {
919       fputs (_("yp_update: cannot get server address\n"), stderr);
920       return YPERR_RPC;
921     }
922
923   switch (ypop)
924     {
925     case YPOP_CHANGE:
926     case YPOP_INSERT:
927     case YPOP_STORE:
928       xdr_argument = (xdrproc_t) xdr_ypupdate_args;
929       break;
930     case YPOP_DELETE:
931       xdr_argument = (xdrproc_t) xdr_ypdelete_args;
932       break;
933     default:
934       return YPERR_BADARGS;
935       break;
936     }
937
938   clnt->cl_auth = authdes_create (servername, WINDOW, &saddr, NULL);
939
940   if (clnt->cl_auth == NULL)
941     clnt->cl_auth = authunix_create_default ();
942
943 again:
944   r = clnt_call (clnt, ypop, xdr_argument, (caddr_t) &args,
945                  (xdrproc_t) xdr_u_int, (caddr_t) &res, RPCTIMEOUT);
946
947   if (r == RPC_AUTHERROR)
948     {
949       if (clnt->cl_auth->ah_cred.oa_flavor == AUTH_DES)
950         {
951           clnt->cl_auth = authunix_create_default ();
952           goto again;
953         }
954       else
955         return YPERR_ACCESS;
956     }
957   if (r != RPC_SUCCESS)
958     {
959       clnt_perror (clnt, "yp_update: clnt_call");
960       return YPERR_RPC;
961     }
962   return res;
963 }