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