Fix UDP resend timeout, fix yp_bind/do_ypcall interaction.
[kopensolaris-gnu/glibc.git] / nis / ypclnt.c
1 /* Copyright (C) 1996, 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>, 1996.
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 <unistd.h>
23 #include <rpc/rpc.h>
24 #include <rpcsvc/nis.h>
25 #include <rpcsvc/yp.h>
26 #include <rpcsvc/ypclnt.h>
27 #include <rpcsvc/ypupd.h>
28 #include <bits/libc-lock.h>
29
30 struct dom_binding
31   {
32     struct dom_binding *dom_pnext;
33     char dom_domain[YPMAXDOMAIN + 1];
34     struct sockaddr_in dom_server_addr;
35     int dom_socket;
36     CLIENT *dom_client;
37     long int dom_vers;
38   };
39 typedef struct dom_binding dom_binding;
40
41 static struct timeval RPCTIMEOUT = {25, 0};
42 static struct timeval UDPTIMEOUT = {5, 0};
43 static int const MAXTRIES = 5;
44 static char __ypdomainname[NIS_MAXNAMELEN + 1] = "\0";
45 __libc_lock_define_initialized (static, ypbindlist_lock)
46 static dom_binding *__ypbindlist = NULL;
47
48
49 static int
50 __yp_bind (const char *domain, dom_binding **ypdb)
51 {
52   struct sockaddr_in clnt_saddr;
53   struct ypbind_resp ypbr;
54   dom_binding *ysd = NULL;
55   int clnt_sock;
56   CLIENT *client;
57   int is_new = 0;
58   int try;
59
60   if ((domain == NULL) || (strlen (domain) == 0))
61     return YPERR_BADARGS;
62
63   if (ypdb != NULL)
64     {
65       ysd = *ypdb;
66       while (ysd != NULL)
67         {
68           if (strcmp (domain, ysd->dom_domain) == 0)
69             break;
70           ysd = ysd->dom_pnext;
71         }
72     }
73
74   if (ysd == NULL)
75     {
76       is_new = 1;
77       ysd = (dom_binding *) malloc (sizeof *ysd);
78       memset (ysd, '\0', sizeof *ysd);
79       ysd->dom_socket = -1;
80       ysd->dom_vers = -1;
81     }
82
83   try = 0;
84
85   do
86     {
87       try++;
88       if (try > MAXTRIES)
89         {
90           if (is_new)
91             free (ysd);
92           return YPERR_YPBIND;
93         }
94
95       if (ysd->dom_vers == -1)
96         {
97           if(ysd->dom_client)
98             {
99               clnt_destroy(ysd->dom_client);
100               ysd->dom_client = NULL;
101               ysd->dom_socket = -1;
102             }
103           memset (&clnt_saddr, '\0', sizeof clnt_saddr);
104           clnt_saddr.sin_family = AF_INET;
105           clnt_saddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
106           clnt_sock = RPC_ANYSOCK;
107           client = clnttcp_create (&clnt_saddr, YPBINDPROG, YPBINDVERS,
108                                    &clnt_sock, 0, 0);
109           if (client == NULL)
110             {
111               if (is_new)
112                 free (ysd);
113               return YPERR_YPBIND;
114             }
115           /*
116           ** Check the port number -- should be < IPPORT_RESERVED.
117           ** If not, it's possible someone has registered a bogus
118           ** ypbind with the portmapper and is trying to trick us.
119           */
120           if (ntohs(clnt_saddr.sin_port) >= IPPORT_RESERVED)
121             {
122               clnt_destroy(client);
123               if (is_new)
124                 free(ysd);
125               return(YPERR_YPBIND);
126             }
127
128           if (clnt_call (client, YPBINDPROC_DOMAIN,
129                          (xdrproc_t) xdr_domainname, (caddr_t) &domain,
130                          (xdrproc_t) xdr_ypbind_resp,
131                          (caddr_t) &ypbr, RPCTIMEOUT) != RPC_SUCCESS)
132             {
133               clnt_destroy (client);
134               close (clnt_sock);
135               if (is_new)
136                 free (ysd);
137               return YPERR_YPBIND;
138             }
139
140           clnt_destroy (client);
141           close (clnt_sock);
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               if (is_new)
148                 free (ysd);
149               return YPERR_DOMAIN;
150             }
151           memset (&ysd->dom_server_addr, '\0', sizeof ysd->dom_server_addr);
152           ysd->dom_server_addr.sin_family = AF_INET;
153           memcpy (&ysd->dom_server_addr.sin_port,
154                   ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port,
155                   sizeof (ysd->dom_server_addr.sin_port));
156           memcpy (&ysd->dom_server_addr.sin_addr.s_addr,
157                   ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr,
158                   sizeof (ysd->dom_server_addr.sin_addr.s_addr));
159           ysd->dom_vers = YPVERS;
160           strncpy (ysd->dom_domain, domain, YPMAXDOMAIN);
161           ysd->dom_domain[YPMAXDOMAIN] = '\0';
162         }
163
164       if (ysd->dom_client)
165         {
166           clnt_destroy (ysd->dom_client);
167           close (ysd->dom_socket);
168         }
169       ysd->dom_socket = RPC_ANYSOCK;
170       ysd->dom_client = clntudp_create (&ysd->dom_server_addr, YPPROG, YPVERS,
171                                         UDPTIMEOUT, &ysd->dom_socket);
172       if (ysd->dom_client == NULL)
173         ysd->dom_vers = -1;
174
175     }
176   while (ysd->dom_client == NULL);
177
178   /* If the program exists, close the socket */
179   if (fcntl (ysd->dom_socket, F_SETFD, 1) == -1)
180     perror (_("fcntl: F_SETFD"));
181
182   if (is_new && ypdb != NULL)
183     {
184       ysd->dom_pnext = *ypdb;
185       *ypdb = ysd;
186     }
187
188   return YPERR_SUCCESS;
189 }
190
191 static void
192 __yp_unbind (dom_binding *ydb)
193 {
194   clnt_destroy (ydb->dom_client);
195   ydb->dom_client = NULL;
196   ydb->dom_socket = -1;
197 }
198
199 static int
200 do_ypcall (const char *domain, u_long prog, xdrproc_t xargs,
201            caddr_t req, xdrproc_t xres, caddr_t resp)
202 {
203   dom_binding *ydb = NULL;
204   bool_t use_ypbindlist = FALSE;
205   int try, result;
206
207   try = 0;
208   result = YPERR_YPERR;
209
210   __libc_lock_lock (ypbindlist_lock);
211   if (__ypbindlist != NULL)
212     {
213       ydb = __ypbindlist;
214       while (ydb != NULL)
215         {
216           if (strcmp (domain, ydb->dom_domain) == 0)
217             break;
218           ydb = ydb->dom_pnext;
219         }
220       if (ydb != NULL)
221         use_ypbindlist = TRUE;
222       else
223         __libc_lock_unlock (ypbindlist_lock);
224     }
225   else
226     __libc_lock_unlock (ypbindlist_lock);
227
228   while (try < MAXTRIES && result != RPC_SUCCESS)
229     {
230       if (__yp_bind (domain, &ydb) != 0)
231         {
232           if (use_ypbindlist)
233             __libc_lock_unlock (ypbindlist_lock);
234           return YPERR_DOMAIN;
235         }
236
237       result = clnt_call (ydb->dom_client, prog,
238                           xargs, req, xres, resp, RPCTIMEOUT);
239
240       if (result != RPC_SUCCESS)
241         {
242           clnt_perror (ydb->dom_client, "do_ypcall: clnt_call");
243           ydb->dom_vers = -1;
244           if (!use_ypbindlist)
245             {
246               __yp_unbind (ydb);
247               free (ydb);
248               ydb = NULL;
249             }
250           result = YPERR_RPC;
251         }
252       try++;
253     }
254   if (use_ypbindlist)
255     {
256       __libc_lock_unlock (ypbindlist_lock);
257       use_ypbindlist = FALSE;
258     }
259   else
260     {
261       __yp_unbind (ydb);
262       free (ydb);
263       ydb = NULL;
264     }
265
266   return result;
267 }
268
269 int
270 yp_bind (const char *indomain)
271 {
272   int status;
273
274   __libc_lock_lock (ypbindlist_lock);
275
276   status = __yp_bind (indomain, &__ypbindlist);
277
278   __libc_lock_unlock (ypbindlist_lock);
279
280   return status;
281 }
282
283 void
284 yp_unbind (const char *indomain)
285 {
286   dom_binding *ydbptr, *ydbptr2;
287
288   __libc_lock_lock (ypbindlist_lock);
289
290   ydbptr2 = NULL;
291   ydbptr = __ypbindlist;
292   while (ydbptr != NULL)
293     {
294       if (strcmp (ydbptr->dom_domain, indomain) == 0)
295         {
296           dom_binding *work;
297
298           work = ydbptr;
299           if (ydbptr2 == NULL)
300             __ypbindlist = __ypbindlist->dom_pnext;
301           else
302             ydbptr2 = ydbptr->dom_pnext;
303           __yp_unbind (work);
304           free (work);
305           break;
306         }
307       ydbptr2 = ydbptr;
308       ydbptr = ydbptr->dom_pnext;
309     }
310
311   __libc_lock_unlock (ypbindlist_lock);
312
313   return;
314 }
315
316 __libc_lock_define_initialized (static, domainname_lock)
317
318 int
319 yp_get_default_domain (char **outdomain)
320 {
321   int result = YPERR_SUCCESS;;
322   *outdomain = NULL;
323
324   __libc_lock_lock (domainname_lock);
325
326   if (__ypdomainname[0] == '\0')
327     {
328       if (getdomainname (__ypdomainname, NIS_MAXNAMELEN))
329         result = YPERR_NODOM;
330       else
331         *outdomain = __ypdomainname;
332     }
333   else
334     *outdomain = __ypdomainname;
335
336   __libc_lock_unlock (domainname_lock);
337
338   return result;
339 }
340
341 int
342 __yp_check (char **domain)
343 {
344   char *unused;
345
346   if (__ypdomainname[0] == '\0')
347     if (yp_get_default_domain (&unused))
348       return 0;
349     else if (strcmp (__ypdomainname, "(none)") == 0)
350       return 0;
351
352   if (domain)
353     *domain = __ypdomainname;
354
355   if (yp_bind (__ypdomainname) == 0)
356     return 1;
357   return 0;
358 }
359
360 int
361 yp_match (const char *indomain, const char *inmap, const char *inkey,
362           const int inkeylen, char **outval, int *outvallen)
363 {
364   ypreq_key req;
365   ypresp_val resp;
366   int result;
367
368   if (indomain == NULL || indomain[0] == '\0' ||
369       inmap == NULL || inmap[0] == '\0' ||
370       inkey == NULL || inkey[0] == '\0' || inkeylen <= 0)
371     return YPERR_BADARGS;
372
373   req.domain = (char *) indomain;
374   req.map = (char *) inmap;
375   req.key.keydat_val = (char *) inkey;
376   req.key.keydat_len = inkeylen;
377
378   *outval = NULL;
379   *outvallen = 0;
380   memset (&resp, '\0', sizeof (resp));
381
382   result = do_ypcall (indomain, YPPROC_MATCH, (xdrproc_t) xdr_ypreq_key,
383                       (caddr_t) & req, (xdrproc_t) xdr_ypresp_val,
384                       (caddr_t) & resp);
385
386   if (result != RPC_SUCCESS)
387     return result;
388   if (resp.stat != YP_TRUE)
389     return ypprot_err (resp.stat);
390
391   *outvallen = resp.val.valdat_len;
392   *outval = malloc (*outvallen + 1);
393   memcpy (*outval, resp.val.valdat_val, *outvallen);
394   (*outval)[*outvallen] = '\0';
395
396   xdr_free ((xdrproc_t) xdr_ypresp_val, (char *) &resp);
397
398   return YPERR_SUCCESS;
399 }
400
401 int
402 yp_first (const char *indomain, const char *inmap, char **outkey,
403           int *outkeylen, char **outval, int *outvallen)
404 {
405   ypreq_nokey req;
406   ypresp_key_val resp;
407   int result;
408
409   if (indomain == NULL || indomain[0] == '\0' ||
410       inmap == NULL || inmap[0] == '\0')
411     return YPERR_BADARGS;
412
413   req.domain = (char *) indomain;
414   req.map = (char *) inmap;
415
416   *outkey = *outval = NULL;
417   *outkeylen = *outvallen = 0;
418   memset (&resp, '\0', sizeof (resp));
419
420   result = do_ypcall (indomain, YPPROC_FIRST, (xdrproc_t) xdr_ypreq_nokey,
421                       (caddr_t) & req, (xdrproc_t) xdr_ypresp_key_val,
422                       (caddr_t) & resp);
423
424   if (result != RPC_SUCCESS)
425     return result;
426   if (resp.stat != YP_TRUE)
427     return ypprot_err (resp.stat);
428
429   *outkeylen = resp.key.keydat_len;
430   *outkey = malloc (*outkeylen + 1);
431   memcpy (*outkey, resp.key.keydat_val, *outkeylen);
432   (*outkey)[*outkeylen] = '\0';
433   *outvallen = resp.val.valdat_len;
434   *outval = malloc (*outvallen + 1);
435   memcpy (*outval, resp.val.valdat_val, *outvallen);
436   (*outval)[*outvallen] = '\0';
437
438   xdr_free ((xdrproc_t) xdr_ypresp_key_val, (char *) &resp);
439
440   return YPERR_SUCCESS;
441 }
442
443 int
444 yp_next (const char *indomain, const char *inmap, const char *inkey,
445          const int inkeylen, char **outkey, int *outkeylen, char **outval,
446          int *outvallen)
447 {
448   ypreq_key req;
449   ypresp_key_val resp;
450   int result;
451
452   if (indomain == NULL || indomain[0] == '\0' ||
453       inmap == NULL || inmap[0] == '\0' ||
454       inkeylen <= 0 || inkey == NULL || inkey[0] == '\0')
455     return YPERR_BADARGS;
456
457   req.domain = (char *) indomain;
458   req.map = (char *) inmap;
459   req.key.keydat_val = (char *) inkey;
460   req.key.keydat_len = inkeylen;
461
462   *outkey = *outval = NULL;
463   *outkeylen = *outvallen = 0;
464   memset (&resp, '\0', sizeof (resp));
465
466   result = do_ypcall (indomain, YPPROC_NEXT, (xdrproc_t) xdr_ypreq_key,
467                       (caddr_t) & req, (xdrproc_t) xdr_ypresp_key_val,
468                       (caddr_t) & resp);
469
470   if (result != RPC_SUCCESS)
471     return result;
472   if (resp.stat != YP_TRUE)
473     return ypprot_err (resp.stat);
474
475   *outkeylen = resp.key.keydat_len;
476   *outkey = malloc (*outkeylen + 1);
477   memcpy (*outkey, resp.key.keydat_val, *outkeylen);
478   (*outkey)[*outkeylen] = '\0';
479   *outvallen = resp.val.valdat_len;
480   *outval = malloc (*outvallen + 1);
481   memcpy (*outval, resp.val.valdat_val, *outvallen);
482   (*outval)[*outvallen] = '\0';
483
484   xdr_free ((xdrproc_t) xdr_ypresp_key_val, (char *) &resp);
485
486   return YPERR_SUCCESS;
487 }
488
489 int
490 yp_master (const char *indomain, const char *inmap, char **outname)
491 {
492   ypreq_nokey req;
493   ypresp_master resp;
494   int result;
495
496   if (indomain == NULL || indomain[0] == '\0' ||
497       inmap == NULL || inmap[0] == '\0')
498     return YPERR_BADARGS;
499
500   req.domain = (char *) indomain;
501   req.map = (char *) inmap;
502
503   memset (&resp, '\0', sizeof (ypresp_master));
504
505   result = do_ypcall (indomain, YPPROC_MASTER, (xdrproc_t) xdr_ypreq_nokey,
506           (caddr_t) & req, (xdrproc_t) xdr_ypresp_master, (caddr_t) & resp);
507
508   if (result != RPC_SUCCESS)
509     return result;
510   if (resp.stat != YP_TRUE)
511     return ypprot_err (resp.stat);
512
513   *outname = strdup (resp.peer);
514   xdr_free ((xdrproc_t) xdr_ypresp_master, (char *) &resp);
515
516   return YPERR_SUCCESS;
517 }
518
519 int
520 yp_order (const char *indomain, const char *inmap, unsigned int *outorder)
521 {
522   struct ypreq_nokey req;
523   struct ypresp_order resp;
524   int result;
525
526   if (indomain == NULL || indomain[0] == '\0' ||
527       inmap == NULL || inmap == '\0')
528     return YPERR_BADARGS;
529
530   req.domain = (char *) indomain;
531   req.map = (char *) inmap;
532
533   memset (&resp, '\0', sizeof (resp));
534
535   result = do_ypcall (indomain, YPPROC_ORDER, (xdrproc_t) xdr_ypreq_nokey,
536            (caddr_t) & req, (xdrproc_t) xdr_ypresp_order, (caddr_t) & resp);
537
538   if (result != RPC_SUCCESS)
539     return result;
540   if (resp.stat != YP_TRUE)
541     return ypprot_err (resp.stat);
542
543   *outorder = resp.ordernum;
544   xdr_free ((xdrproc_t) xdr_ypresp_order, (char *) &resp);
545
546   return YPERR_SUCCESS;
547 }
548
549 static void *ypall_data;
550 static int (*ypall_foreach) __P ((int status, char *key, int keylen,
551                                   char *val, int vallen, char *data));
552
553 static bool_t
554 __xdr_ypresp_all (XDR * xdrs, u_long * objp)
555 {
556   while (1)
557     {
558       struct ypresp_all resp;
559
560       memset (&resp, '\0', sizeof (struct ypresp_all));
561       if (!xdr_ypresp_all (xdrs, &resp))
562         {
563           xdr_free ((xdrproc_t) xdr_ypresp_all, (char *) &resp);
564           *objp = YP_YPERR;
565           return (FALSE);
566         }
567       if (resp.more == 0)
568         {
569           xdr_free ((xdrproc_t) xdr_ypresp_all, (char *) &resp);
570           *objp = YP_NOMORE;
571           return (FALSE);
572         }
573
574       switch (resp.ypresp_all_u.val.stat)
575         {
576         case YP_TRUE:
577           {
578             char key[resp.ypresp_all_u.val.key.keydat_len + 1];
579             char val[resp.ypresp_all_u.val.val.valdat_len + 1];
580             int keylen = resp.ypresp_all_u.val.key.keydat_len;
581             int vallen = resp.ypresp_all_u.val.val.valdat_len;
582
583             *objp = YP_TRUE;
584             memcpy (key, resp.ypresp_all_u.val.key.keydat_val, keylen);
585             key[keylen] = '\0';
586             memcpy (val, resp.ypresp_all_u.val.val.valdat_val, vallen);
587             val[vallen] = '\0';
588             xdr_free ((xdrproc_t) xdr_ypresp_all, (char *) &resp);
589             if ((*ypall_foreach) (*objp, key, keylen,
590                                   val, vallen, ypall_data))
591               return TRUE;
592           }
593           break;
594         case YP_NOMORE:
595           *objp = YP_NOMORE;
596           xdr_free ((xdrproc_t) xdr_ypresp_all, (char *) &resp);
597           return TRUE;
598           break;
599         default:
600           *objp = resp.ypresp_all_u.val.stat;
601           xdr_free ((xdrproc_t) xdr_ypresp_all, (char *) &resp);
602           return TRUE;
603         }
604     }
605 }
606
607 int
608 yp_all (const char *indomain, const char *inmap,
609         const struct ypall_callback *incallback)
610 {
611   struct ypreq_nokey req;
612   dom_binding *ydb = NULL;
613   int try, result;
614   struct sockaddr_in clnt_sin;
615   CLIENT *clnt;
616   unsigned long status;
617   int clnt_sock;
618
619   if (indomain == NULL || indomain[0] == '\0' ||
620       inmap == NULL || inmap == '\0')
621     return YPERR_BADARGS;
622
623   try = 0;
624   result = YPERR_YPERR;
625
626   while (try < MAXTRIES && result != RPC_SUCCESS)
627     {
628       if (__yp_bind (indomain, &ydb) != 0)
629         {
630           return YPERR_DOMAIN;
631         }
632
633       /* YPPROC_ALL get its own TCP channel to ypserv */
634       clnt_sock = RPC_ANYSOCK;
635       clnt_sin = ydb->dom_server_addr;
636       clnt_sin.sin_port = 0;
637       clnt = clnttcp_create (&clnt_sin, YPPROG, YPVERS, &clnt_sock, 0, 0);
638       if (clnt == NULL)
639         {
640           puts (_("yp_all: clnttcp_create failed"));
641           return YPERR_PMAP;
642         }
643       req.domain = (char *) indomain;
644       req.map = (char *) inmap;
645
646       ypall_foreach = incallback->foreach;
647       ypall_data = (void *) incallback->data;
648
649       result = clnt_call (clnt, YPPROC_ALL, (xdrproc_t) xdr_ypreq_nokey,
650                           (caddr_t) &req, (xdrproc_t) __xdr_ypresp_all,
651                           (caddr_t) &status, RPCTIMEOUT);
652
653       clnt_destroy (clnt);
654       close (clnt_sock);
655       if (result != RPC_SUCCESS)
656         {
657           clnt_perror (ydb->dom_client, "yp_all: clnt_call");
658           __yp_unbind (ydb);
659           free (ydb);
660           result = YPERR_RPC;
661         }
662       else
663         result = YPERR_SUCCESS;
664
665       if (status != YP_NOMORE)
666         return ypprot_err (status);
667       try++;
668     }
669
670   return result;
671 }
672
673 int
674 yp_maplist (const char *indomain, struct ypmaplist **outmaplist)
675 {
676   struct ypresp_maplist resp;
677   int result;
678
679   if (indomain == NULL || indomain[0] == '\0')
680     return YPERR_BADARGS;
681
682   memset (&resp, '\0', sizeof (resp));
683
684   result = do_ypcall (indomain, YPPROC_MAPLIST, (xdrproc_t) xdr_domainname,
685     (caddr_t) & indomain, (xdrproc_t) xdr_ypresp_maplist, (caddr_t) & resp);
686
687   if (result != RPC_SUCCESS)
688     return result;
689   if (resp.stat != YP_TRUE)
690     return ypprot_err (resp.stat);
691
692   *outmaplist = resp.maps;
693   /* We give the list not free, this will be done by ypserv
694      xdr_free((xdrproc_t)xdr_ypresp_maplist, (char *)&resp); */
695
696   return YPERR_SUCCESS;
697 }
698
699 const char *
700 yperr_string (const int error)
701 {
702   switch (error)
703     {
704     case YPERR_SUCCESS:
705       return _("Success");
706     case YPERR_BADARGS:
707       return _("Request arguments bad");
708     case YPERR_RPC:
709       return _("RPC failure on NIS operation");
710     case YPERR_DOMAIN:
711       return _("Can't bind to server which serves this domain");
712     case YPERR_MAP:
713       return _("No such map in server's domain");
714     case YPERR_KEY:
715       return _("No such key in map");
716     case YPERR_YPERR:
717       return _("Internal NIS error");
718     case YPERR_RESRC:
719       return _("Local resource allocation failure");
720     case YPERR_NOMORE:
721       return _("No more records in map database");
722     case YPERR_PMAP:
723       return _("Can't communicate with portmapper");
724     case YPERR_YPBIND:
725       return _("Can't communicate with ypbind");
726     case YPERR_YPSERV:
727       return _("Can't communicate with ypserv");
728     case YPERR_NODOM:
729       return _("Local domain name not set");
730     case YPERR_BADDB:
731       return _("NIS map data base is bad");
732     case YPERR_VERS:
733       return _("NIS client/server version mismatch - can't supply service");
734     case YPERR_ACCESS:
735       return _("Permission denied");
736     case YPERR_BUSY:
737       return _("Database is busy");
738     }
739   return _("Unknown NIS error code");
740 }
741
742 int
743 ypprot_err (const int code)
744 {
745   switch (code)
746     {
747     case YP_TRUE:
748       return YPERR_SUCCESS;
749     case YP_NOMORE:
750       return YPERR_NOMORE;
751     case YP_FALSE:
752       return YPERR_YPERR;
753     case YP_NOMAP:
754       return YPERR_MAP;
755     case YP_NODOM:
756       return YPERR_DOMAIN;
757     case YP_NOKEY:
758       return YPERR_KEY;
759     case YP_BADOP:
760       return YPERR_YPERR;
761     case YP_BADDB:
762       return YPERR_BADDB;
763     case YP_YPERR:
764       return YPERR_YPERR;
765     case YP_BADARGS:
766       return YPERR_BADARGS;
767     case YP_VERS:
768       return YPERR_VERS;
769     }
770   return YPERR_YPERR;
771 }
772
773 const char *
774 ypbinderr_string (const int error)
775 {
776   switch (error)
777     {
778     case 0:
779       return _("Success");
780     case YPBIND_ERR_ERR:
781       return _("Internal ypbind error");
782     case YPBIND_ERR_NOSERV:
783       return _("Domain not bound");
784     case YPBIND_ERR_RESC:
785       return _("System resource allocation failure");
786     default:
787       return _("Unknown ypbind error");
788     }
789 }
790
791
792 #define WINDOW 60
793
794 int
795 yp_update (char *domain, char *map, unsigned ypop,
796            char *key, int keylen, char *data, int datalen)
797 {
798   union
799     {
800       ypupdate_args update_args;
801       ypdelete_args delete_args;
802     }
803   args;
804   xdrproc_t xdr_argument;
805   unsigned res = 0;
806   CLIENT *clnt;
807   char *master;
808   struct sockaddr saddr;
809   char servername[MAXNETNAMELEN + 1];
810   int r;
811
812   if (!domain || !map || !key || (ypop != YPOP_DELETE && !data))
813     return YPERR_BADARGS;
814
815   args.update_args.mapname = map;
816   args.update_args.key.yp_buf_len = keylen;
817   args.update_args.key.yp_buf_val = key;
818   args.update_args.datum.yp_buf_len = datalen;
819   args.update_args.datum.yp_buf_val = data;
820
821   if ((r = yp_master (domain, map, &master)) != 0)
822     return r;
823
824   if (!host2netname (servername, master, domain))
825     {
826       fputs (_("yp_update: cannot convert host to netname\n"), stderr);
827       return YPERR_YPERR;
828     }
829
830   if ((clnt = clnt_create (master, YPU_PROG, YPU_VERS, "tcp")) == NULL)
831     {
832       clnt_pcreateerror ("yp_update: clnt_create");
833       return YPERR_RPC;
834     }
835
836   if (!clnt_control (clnt, CLGET_SERVER_ADDR, (char *) &saddr))
837     {
838       fputs (_("yp_update: cannot get server address\n"), stderr);
839       return YPERR_RPC;
840     }
841
842   switch (ypop)
843     {
844     case YPOP_CHANGE:
845     case YPOP_INSERT:
846     case YPOP_STORE:
847       xdr_argument = (xdrproc_t) xdr_ypupdate_args;
848       break;
849     case YPOP_DELETE:
850       xdr_argument = (xdrproc_t) xdr_ypdelete_args;
851       break;
852     default:
853       return YPERR_BADARGS;
854       break;
855     }
856
857   clnt->cl_auth = authdes_create (servername, WINDOW, &saddr, NULL);
858
859   if (clnt->cl_auth == NULL)
860     clnt->cl_auth = authunix_create_default ();
861
862 again:
863   r = clnt_call (clnt, ypop, xdr_argument, (caddr_t) &args,
864                  (xdrproc_t) xdr_u_int, (caddr_t) &res, RPCTIMEOUT);
865
866   if (r == RPC_AUTHERROR)
867     {
868       if (clnt->cl_auth->ah_cred.oa_flavor == AUTH_DES)
869         {
870           clnt->cl_auth = authunix_create_default ();
871           goto again;
872         }
873       else
874         return YPERR_ACCESS;
875     }
876   if (r != RPC_SUCCESS)
877     {
878       clnt_perror (clnt, "yp_update: clnt_call");
879       return YPERR_RPC;
880     }
881   return res;
882 }