nrelease - fix/improve livecd
[dragonfly.git] / lib / libc / yp / yplib.c
1 /*
2  * Copyright (c) 1992/3 Theo de Raadt <deraadt@fsa.ca>
3  * Copyright (c) 1998 Bill Paul <wpaul@ctr.columbia.edu>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote
15  *    products derived from this software without specific prior written
16  *    permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
19  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $FreeBSD: src/lib/libc/yp/yplib.c,v 1.51 2007/07/24 13:06:08 simon Exp $
31  */
32
33 #include "namespace.h"
34 #include "reentrant.h"
35 #include <sys/param.h>
36 #include <sys/types.h>
37 #include <sys/socket.h>
38 #include <sys/uio.h>
39 #include <arpa/inet.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46 #include <rpc/rpc.h>
47 #include <rpc/xdr.h>
48 #include <rpcsvc/yp.h>
49 #include "un-namespace.h"
50 #include "libc_private.h"
51
52 bool_t  xdr_ypresp_all_seq(XDR *, u_long *);
53
54 int (*ypresp_allfn)(unsigned long, char *, int, char *, int, void *);
55
56 /*
57  * We have to define these here due to clashes between yp_prot.h and
58  * yp.h.
59  */
60
61 #define YPMATCHCACHE
62
63 #ifdef YPMATCHCACHE
64 struct ypmatch_ent {
65         char                    *ypc_map;
66         keydat                  ypc_key;
67         valdat                  ypc_val;
68         time_t                  ypc_expire_t;
69         struct ypmatch_ent      *ypc_next;
70 };
71 #define YPLIB_MAXCACHE  5       /* At most 5 entries */
72 #define YPLIB_EXPIRE    5       /* Expire after 5 seconds */
73 #endif
74
75 struct dom_binding {
76         struct dom_binding *dom_pnext;
77         char dom_domain[YPMAXDOMAIN + 1];
78         struct sockaddr_in dom_server_addr;
79         u_short dom_server_port;
80         int dom_socket;
81         CLIENT *dom_client;
82         u_short dom_local_port; /* now I finally know what this is for. */
83         long dom_vers;
84 #ifdef YPMATCHCACHE
85         struct ypmatch_ent *cache;
86         int ypmatch_cachecnt;
87 #endif
88 };
89
90 #include <rpcsvc/yp.h>
91 #include <rpcsvc/ypclnt.h>
92
93 #ifndef BINDINGDIR
94 #define BINDINGDIR "/var/yp/binding"
95 #endif
96 #define MAX_RETRIES 20
97
98 void *ypresp_data;
99
100 static void _yp_unbind(struct dom_binding *);
101 struct dom_binding *_ypbindlist;
102 static char _yp_domain[MAXHOSTNAMELEN];
103 int _yplib_timeout = 20;
104
105 static mutex_t _ypmutex = MUTEX_INITIALIZER;
106 #define YPLOCK()        mutex_lock(&_ypmutex);
107 #define YPUNLOCK()      mutex_unlock(&_ypmutex);
108
109 #ifdef YPMATCHCACHE
110 static void
111 ypmatch_cache_delete(struct dom_binding *ypdb, struct ypmatch_ent *prev,
112     struct ypmatch_ent *cur)
113 {
114         if (prev == NULL)
115                 ypdb->cache = cur->ypc_next;
116         else
117                 prev->ypc_next = cur->ypc_next;
118
119         free(cur->ypc_map);
120         free(cur->ypc_key.keydat_val);
121         free(cur->ypc_val.valdat_val);
122         free(cur);
123
124         ypdb->ypmatch_cachecnt--;
125
126         return;
127 }
128
129 static void
130 ypmatch_cache_flush(struct dom_binding *ypdb)
131 {
132         struct ypmatch_ent      *n, *c = ypdb->cache;
133
134         while (c != NULL) {
135                 n = c->ypc_next;
136                 ypmatch_cache_delete(ypdb, NULL, c);
137                 c = n;
138         }
139
140         return;
141 }
142
143 static void
144 ypmatch_cache_expire(struct dom_binding *ypdb)
145 {
146         struct ypmatch_ent      *c = ypdb->cache;
147         struct ypmatch_ent      *n, *p = NULL;
148         time_t                  t;
149
150         time(&t);
151
152         while (c != NULL) {
153                 if (t >= c->ypc_expire_t) {
154                         n = c->ypc_next;
155                         ypmatch_cache_delete(ypdb, p, c);
156                         c = n;
157                 } else {
158                         p = c;
159                         c = c->ypc_next;
160                 }
161         }
162
163         return;
164 }
165
166 static void
167 ypmatch_cache_insert(struct dom_binding *ypdb, char *map, keydat *key,
168     valdat *val)
169 {
170         struct ypmatch_ent      *new;
171
172         /* Do an expire run to maybe open up a slot. */
173         if (ypdb->ypmatch_cachecnt)
174                 ypmatch_cache_expire(ypdb);
175
176         /*
177          * If there are no slots free, then force an expire of
178          * the least recently used entry.
179          */
180         if (ypdb->ypmatch_cachecnt >= YPLIB_MAXCACHE) {
181                 struct ypmatch_ent      *o = NULL, *c = ypdb->cache;
182                 time_t                  oldest = 0;
183
184                 oldest = ~oldest;
185
186                 while (c != NULL) {
187                         if (c->ypc_expire_t < oldest) {
188                                 oldest = c->ypc_expire_t;
189                                 o = c;
190                         }
191                         c = c->ypc_next;
192                 }
193
194                 if (o == NULL)
195                         return;
196                 o->ypc_expire_t = 0;
197                 ypmatch_cache_expire(ypdb);
198         }
199
200         new = malloc(sizeof(struct ypmatch_ent));
201         if (new == NULL)
202                 return;
203
204         new->ypc_map = strdup(map);
205         if (new->ypc_map == NULL) {
206                 free(new);
207                 return;
208         }
209         new->ypc_key.keydat_val = malloc(key->keydat_len);
210         if (new->ypc_key.keydat_val == NULL) {
211                 free(new->ypc_map);
212                 free(new);
213                 return;
214         }
215         new->ypc_val.valdat_val = malloc(val->valdat_len);
216         if (new->ypc_val.valdat_val == NULL) {
217                 free(new->ypc_val.valdat_val);
218                 free(new->ypc_map);
219                 free(new);
220                 return;
221         }
222
223         new->ypc_expire_t = time(NULL) + YPLIB_EXPIRE;
224         new->ypc_key.keydat_len = key->keydat_len;
225         new->ypc_val.valdat_len = val->valdat_len;
226         bcopy(key->keydat_val, new->ypc_key.keydat_val, key->keydat_len);
227         bcopy(val->valdat_val, new->ypc_val.valdat_val, val->valdat_len);
228
229         new->ypc_next = ypdb->cache;
230         ypdb->cache = new;
231
232         ypdb->ypmatch_cachecnt++;
233
234         return;
235 }
236
237 static bool_t
238 ypmatch_cache_lookup(struct dom_binding *ypdb, char *map, keydat *key,
239     valdat *val)
240 {
241         struct ypmatch_ent      *c = ypdb->cache;
242
243         ypmatch_cache_expire(ypdb);
244
245         for (c = ypdb->cache; c != NULL; c = c->ypc_next) {
246                 if (strcmp(map, c->ypc_map))
247                         continue;
248                 if (key->keydat_len != c->ypc_key.keydat_len)
249                         continue;
250                 if (bcmp(key->keydat_val, c->ypc_key.keydat_val,
251                                 key->keydat_len))
252                         continue;
253         }
254
255         if (c == NULL)
256                 return(FALSE);
257
258         val->valdat_len = c->ypc_val.valdat_len;
259         val->valdat_val = c->ypc_val.valdat_val;
260
261         return(TRUE);
262 }
263 #endif
264
265 char *
266 ypbinderr_string(int incode)
267 {
268         const char *errstr;
269         static char err[80];
270         switch (incode) {
271         case 0:
272                 errstr = "Success";
273                 break;
274         case YPBIND_ERR_ERR:
275                 errstr = "Internal ypbind error";
276                 break;
277         case YPBIND_ERR_NOSERV:
278                 errstr = "Domain not bound";
279                 break;
280         case YPBIND_ERR_RESC:
281                 errstr = "System resource allocation failure";
282                 break;
283         default:
284                 errstr = NULL;
285                 break;
286         }
287         if (errstr != NULL)
288                 strlcpy(err, errstr, sizeof(err));
289         else
290                 snprintf(err, sizeof(err), "Unknown ypbind error: #%d\n", incode);
291         return (err);
292 }
293
294 int
295 _yp_dobind(const char *dom, struct dom_binding **ypdb)
296 {
297         static pid_t pid = -1;
298         char path[MAXPATHLEN];
299         struct dom_binding *ysd, *ysd2;
300         struct ypbind_resp ypbr;
301         struct timeval tv;
302         struct sockaddr_in clnt_sin;
303         int clnt_sock, fd;
304         pid_t gpid;
305         CLIENT *client;
306         int new = 0;
307         ssize_t r;
308         int retries = 0;
309         struct sockaddr_in check;
310         socklen_t checklen = sizeof(struct sockaddr_in);
311
312         /* Not allowed; bad doggie. Bad. */
313         if (strchr(dom, '/') != NULL)
314                 return(YPERR_BADARGS);
315
316         gpid = getpid();
317         if (!(pid == -1 || pid == gpid)) {
318                 ysd = _ypbindlist;
319                 while (ysd) {
320                         if (ysd->dom_client != NULL)
321                                 _yp_unbind(ysd);
322                         ysd2 = ysd->dom_pnext;
323                         free(ysd);
324                         ysd = ysd2;
325                 }
326                 _ypbindlist = NULL;
327         }
328         pid = gpid;
329
330         if (ypdb != NULL)
331                 *ypdb = NULL;
332
333         if (dom == NULL || strlen(dom) == 0)
334                 return (YPERR_BADARGS);
335
336         for (ysd = _ypbindlist; ysd; ysd = ysd->dom_pnext)
337                 if (strcmp(dom, ysd->dom_domain) == 0)
338                         break;
339
340
341         if (ysd == NULL) {
342                 ysd = (struct dom_binding *)malloc(sizeof *ysd);
343                 bzero((char *)ysd, sizeof *ysd);
344                 ysd->dom_socket = -1;
345                 ysd->dom_vers = 0;
346                 new = 1;
347         } else {
348         /* Check the socket -- may have been hosed by the caller. */
349                 if (_getsockname(ysd->dom_socket, (struct sockaddr *)&check,
350                     &checklen) == -1 || check.sin_family != AF_INET ||
351                     check.sin_port != ysd->dom_local_port) {
352                 /* Socket became bogus somehow... need to rebind. */
353                         int save, sock;
354
355                         sock = ysd->dom_socket;
356                         save = _dup(ysd->dom_socket);
357                         if (ysd->dom_client != NULL)
358                                 clnt_destroy(ysd->dom_client);
359                         ysd->dom_vers = 0;
360                         ysd->dom_client = NULL;
361                         sock = _dup2(save, sock);
362                         _close(save);
363                 }
364         }
365
366 again:
367         retries++;
368         if (retries > MAX_RETRIES) {
369                 if (new)
370                         free(ysd);
371                 return(YPERR_YPBIND);
372         }
373 #ifdef BINDINGDIR
374         if (ysd->dom_vers == 0) {
375                 /*
376                  * We're trying to make a new binding: zorch the
377                  * existing handle now (if any).
378                  */
379                 if (ysd->dom_client != NULL) {
380                         clnt_destroy(ysd->dom_client);
381                         ysd->dom_client = NULL;
382                         ysd->dom_socket = -1;
383                 }
384                 snprintf(path, sizeof(path), "%s/%s.%d", BINDINGDIR, dom, 2);
385                 if ((fd = _open(path, O_RDONLY | O_CLOEXEC)) == -1) {
386                         /* no binding file, YP is dead. */
387                         /* Try to bring it back to life. */
388                         _close(fd);
389                         goto skipit;
390                 }
391                 if (_flock(fd, LOCK_EX|LOCK_NB) == -1 && errno == EWOULDBLOCK) {
392                         struct iovec iov[2];
393                         struct ypbind_resp ybr;
394                         u_short ypb_port;
395
396                         iov[0].iov_base = (caddr_t)&ypb_port;
397                         iov[0].iov_len = sizeof ypb_port;
398                         iov[1].iov_base = (caddr_t)&ybr;
399                         iov[1].iov_len = sizeof ybr;
400
401                         r = _readv(fd, iov, 2);
402                         if (r != (ssize_t)(iov[0].iov_len + iov[1].iov_len)) {
403                                 _close(fd);
404                                 ysd->dom_vers = -1;
405                                 goto again;
406                         }
407
408                         bzero(&ysd->dom_server_addr, sizeof ysd->dom_server_addr);
409                         ysd->dom_server_addr.sin_family = AF_INET;
410                         ysd->dom_server_addr.sin_len = sizeof(struct sockaddr_in);
411                         bcopy(&ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr,
412                             &ysd->dom_server_addr.sin_addr.s_addr,
413                             sizeof(ysd->dom_server_addr.sin_addr.s_addr));
414                         bcopy(&ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port,
415                             &ysd->dom_server_addr.sin_port,
416                             sizeof(ysd->dom_server_addr.sin_port));
417
418                         ysd->dom_server_port = ysd->dom_server_addr.sin_port;
419                         _close(fd);
420                         goto gotit;
421                 } else {
422                         /* no lock on binding file, YP is dead. */
423                         /* Try to bring it back to life. */
424                         _close(fd);
425                         goto skipit;
426                 }
427         }
428 skipit:
429 #endif
430         if (ysd->dom_vers == -1 || ysd->dom_vers == 0) {
431                 /*
432                  * We're trying to make a new binding: zorch the
433                  * existing handle now (if any).
434                  */
435                 if (ysd->dom_client != NULL) {
436                         clnt_destroy(ysd->dom_client);
437                         ysd->dom_client = NULL;
438                         ysd->dom_socket = -1;
439                 }
440                 bzero((char *)&clnt_sin, sizeof clnt_sin);
441                 clnt_sin.sin_family = AF_INET;
442                 clnt_sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
443
444                 clnt_sock = RPC_ANYSOCK;
445                 client = clnttcp_create(&clnt_sin, YPBINDPROG, YPBINDVERS, &clnt_sock,
446                         0, 0);
447                 if (client == NULL) {
448                         /*
449                          * These conditions indicate ypbind just isn't
450                          * alive -- we probably don't want to shoot our
451                          * mouth off in this case; instead generate error
452                          * messages only for really exotic problems.
453                          */
454                         if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED &&
455                            (rpc_createerr.cf_stat != RPC_SYSTEMERROR &&
456                            rpc_createerr.cf_error.re_errno == ECONNREFUSED))
457                                 clnt_pcreateerror("clnttcp_create");
458                         if (new)
459                                 free(ysd);
460                         return (YPERR_YPBIND);
461                 }
462
463                 /*
464                  * Check the port number -- should be < IPPORT_RESERVED.
465                  * If not, it's possible someone has registered a bogus
466                  * ypbind with the portmapper and is trying to trick us.
467                  */
468                 if (ntohs(clnt_sin.sin_port) >= IPPORT_RESERVED) {
469                         if (client != NULL)
470                                 clnt_destroy(client);
471                         if (new)
472                                 free(ysd);
473                         return(YPERR_YPBIND);
474                 }
475                 tv.tv_sec = _yplib_timeout/2;
476                 tv.tv_usec = 0;
477                 r = clnt_call(client, YPBINDPROC_DOMAIN,
478                     (xdrproc_t)xdr_domainname, &dom,
479                     (xdrproc_t)xdr_ypbind_resp, &ypbr, tv);
480                 if (r != RPC_SUCCESS) {
481                         clnt_destroy(client);
482                         ysd->dom_vers = -1;
483                         if (r == RPC_PROGUNAVAIL || r == RPC_PROCUNAVAIL) {
484                                 if (new)
485                                         free(ysd);
486                                 return(YPERR_YPBIND);
487                         }
488                         fprintf(stderr,
489                         "YP: server for domain %s not responding, retrying\n", dom);
490                         goto again;
491                 } else {
492                         if (ypbr.ypbind_status != YPBIND_SUCC_VAL) {
493                                 struct timespec time_to_sleep, time_remaining;
494
495                                 clnt_destroy(client);
496                                 ysd->dom_vers = -1;
497
498                                 time_to_sleep.tv_sec = _yplib_timeout/2;
499                                 time_to_sleep.tv_nsec = 0;
500                                 _nanosleep(&time_to_sleep,
501                                     &time_remaining);
502                                 goto again;
503                         }
504                 }
505                 clnt_destroy(client);
506
507                 bzero((char *)&ysd->dom_server_addr, sizeof ysd->dom_server_addr);
508                 ysd->dom_server_addr.sin_family = AF_INET;
509                 bcopy(&ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port,
510                     &ysd->dom_server_addr.sin_port,
511                     sizeof(ysd->dom_server_addr.sin_port));
512                 bcopy(&ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr,
513                     &ysd->dom_server_addr.sin_addr.s_addr,
514                     sizeof(ysd->dom_server_addr.sin_addr.s_addr));
515
516                 /*
517                  * We could do a reserved port check here too, but this
518                  * could pose compatibility problems. The local ypbind is
519                  * supposed to decide whether or not to trust yp servers
520                  * on insecure ports. For now, we trust its judgement.
521                  */
522                 ysd->dom_server_port =
523                         *(u_short *)&ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port;
524 gotit:
525                 ysd->dom_vers = YPVERS;
526                 strlcpy(ysd->dom_domain, dom, sizeof(ysd->dom_domain));
527         }
528
529         /* Don't rebuild the connection to the server unless we have to. */
530         if (ysd->dom_client == NULL) {
531                 tv.tv_sec = _yplib_timeout/2;
532                 tv.tv_usec = 0;
533                 ysd->dom_socket = RPC_ANYSOCK;
534                 ysd->dom_client = clntudp_bufcreate(&ysd->dom_server_addr,
535                         YPPROG, YPVERS, tv, &ysd->dom_socket, 1280, 2304);
536                 if (ysd->dom_client == NULL) {
537                         clnt_pcreateerror("clntudp_create");
538                         ysd->dom_vers = -1;
539                         goto again;
540                 }
541                 if (_fcntl(ysd->dom_socket, F_SETFD, 1) == -1)
542                         perror("fcntl: F_SETFD");
543                 /*
544                  * We want a port number associated with this socket
545                  * so that we can check its authenticity later.
546                  */
547                 checklen = sizeof(struct sockaddr_in);
548                 bzero((char *)&check, checklen);
549                 _bind(ysd->dom_socket, (struct sockaddr *)&check, checklen);
550                 check.sin_family = AF_INET;
551                 if (!_getsockname(ysd->dom_socket,
552                     (struct sockaddr *)&check, &checklen)) {
553                         ysd->dom_local_port = check.sin_port;
554                 } else {
555                         clnt_destroy(ysd->dom_client);
556                         if (new)
557                                 free(ysd);
558                         return(YPERR_YPBIND);
559                 }
560         }
561
562         if (new) {
563                 ysd->dom_pnext = _ypbindlist;
564                 _ypbindlist = ysd;
565         }
566
567         /*
568          * Set low retry timeout to realistically handle UDP packet
569          * loss for YP packet bursts.
570          */
571         tv.tv_sec = 1;
572         tv.tv_usec = 0;
573         clnt_control(ysd->dom_client, CLSET_RETRY_TIMEOUT, (char*)&tv);
574
575         if (ypdb != NULL)
576                 *ypdb = ysd;
577         return (0);
578 }
579
580 static void
581 _yp_unbind(struct dom_binding *ypb)
582 {
583         struct sockaddr_in check;
584         socklen_t checklen = sizeof(struct sockaddr_in);
585
586         if (ypb->dom_client != NULL) {
587                 /* Check the socket -- may have been hosed by the caller. */
588                 if (_getsockname(ypb->dom_socket, (struct sockaddr *)&check,
589                 &checklen) == -1 || check.sin_family != AF_INET ||
590                 check.sin_port != ypb->dom_local_port) {
591                         int save, sock;
592
593                         sock = ypb->dom_socket;
594                         save = _dup(ypb->dom_socket);
595                         clnt_destroy(ypb->dom_client);
596                         sock = _dup2(save, sock);
597                         _close(save);
598                 } else
599                         clnt_destroy(ypb->dom_client);
600         }
601
602         ypb->dom_client = NULL;
603         ypb->dom_socket = -1;
604         ypb->dom_vers = -1;
605 #ifdef YPMATCHCACHE
606         ypmatch_cache_flush(ypb);
607 #endif
608 }
609
610 static int
611 yp_bind_locked(char *dom)
612 {
613         return (_yp_dobind(dom, NULL));
614 }
615
616 int
617 yp_bind(char *dom)
618 {
619         int r;
620
621         YPLOCK();
622         r = yp_bind_locked(dom);
623         YPUNLOCK();
624         return (r);
625 }
626
627 static void
628 yp_unbind_locked(char *dom)
629 {
630         struct dom_binding *ypb, *ypbp;
631
632         ypbp = NULL;
633         for (ypb = _ypbindlist; ypb; ypb = ypb->dom_pnext) {
634                 if (strcmp(dom, ypb->dom_domain) == 0) {
635                         _yp_unbind(ypb);
636                         if (ypbp)
637                                 ypbp->dom_pnext = ypb->dom_pnext;
638                         else
639                                 _ypbindlist = ypb->dom_pnext;
640                         free(ypb);
641                         return;
642                 }
643                 ypbp = ypb;
644         }
645         return;
646 }
647
648 void
649 yp_unbind(char *dom)
650 {
651         YPLOCK();
652         yp_unbind_locked(dom);
653         YPUNLOCK();
654 }
655
656 int
657 yp_match(char *indomain, char *inmap, const char *inkey, int inkeylen,
658     char **outval, int *outvallen)
659 {
660         struct dom_binding *ysd;
661         struct ypresp_val yprv;
662         struct timeval tv;
663         struct ypreq_key yprk;
664         int r;
665
666         *outval = NULL;
667         *outvallen = 0;
668
669         /* Sanity check */
670
671         if (inkey == NULL || !strlen(inkey) || inkeylen <= 0 ||
672             inmap == NULL || !strlen(inmap) ||
673             indomain == NULL || !strlen(indomain))
674                 return (YPERR_BADARGS);
675
676         YPLOCK();
677         if (_yp_dobind(indomain, &ysd) != 0) {
678                 YPUNLOCK();
679                 return(YPERR_DOMAIN);
680         }
681
682         yprk.domain = indomain;
683         yprk.map = inmap;
684         yprk.key.keydat_val = (char *)inkey;
685         yprk.key.keydat_len = inkeylen;
686
687 #ifdef YPMATCHCACHE
688         if (ypmatch_cache_lookup(ysd, yprk.map, &yprk.key, &yprv.val) == TRUE) {
689 /*
690         if (!strcmp(_yp_domain, indomain) && ypmatch_find(inmap, inkey,
691             inkeylen, &yprv.val.valdat_val, &yprv.val.valdat_len)) {
692 */
693                 *outvallen = yprv.val.valdat_len;
694                 *outval = (char *)malloc(*outvallen+1);
695                 bcopy(yprv.val.valdat_val, *outval, *outvallen);
696                 (*outval)[*outvallen] = '\0';
697                 YPUNLOCK();
698                 return (0);
699         }
700 #endif
701
702 again:
703         if (_yp_dobind(indomain, &ysd) != 0) {
704                 YPUNLOCK();
705                 return (YPERR_DOMAIN);
706         }
707
708         tv.tv_sec = _yplib_timeout;
709         tv.tv_usec = 0;
710
711         bzero((char *)&yprv, sizeof yprv);
712
713         r = clnt_call(ysd->dom_client, YPPROC_MATCH,
714             (xdrproc_t)xdr_ypreq_key, &yprk,
715             (xdrproc_t)xdr_ypresp_val, &yprv, tv);
716         if (r != RPC_SUCCESS) {
717                 clnt_perror(ysd->dom_client, "yp_match: clnt_call");
718                 _yp_unbind(ysd);
719                 goto again;
720         }
721
722         if (!(r = ypprot_err(yprv.stat))) {
723                 *outvallen = yprv.val.valdat_len;
724                 *outval = (char *)malloc(*outvallen+1);
725                 bcopy(yprv.val.valdat_val, *outval, *outvallen);
726                 (*outval)[*outvallen] = '\0';
727 #ifdef YPMATCHCACHE
728                 ypmatch_cache_insert(ysd, yprk.map, &yprk.key, &yprv.val);
729 #endif
730         }
731
732         xdr_free((xdrproc_t)xdr_ypresp_val, &yprv);
733         YPUNLOCK();
734         return (r);
735 }
736
737 static int
738 yp_get_default_domain_locked(char **domp)
739 {
740         *domp = NULL;
741         if (_yp_domain[0] == '\0')
742                 if (getdomainname(_yp_domain, sizeof _yp_domain))
743                         return (YPERR_NODOM);
744         *domp = _yp_domain;
745         return (0);
746 }
747
748 int
749 yp_get_default_domain(char **domp)
750 {
751         int r;
752
753         YPLOCK();
754         r = yp_get_default_domain_locked(domp);
755         YPUNLOCK();
756         return (r);
757 }
758
759 int
760 yp_first(char *indomain, char *inmap, char **outkey, int *outkeylen,
761     char **outval, int *outvallen)
762 {
763         struct ypresp_key_val yprkv;
764         struct ypreq_nokey yprnk;
765         struct dom_binding *ysd;
766         struct timeval tv;
767         int r;
768
769         /* Sanity check */
770
771         if (indomain == NULL || !strlen(indomain) ||
772             inmap == NULL || !strlen(inmap))
773                 return (YPERR_BADARGS);
774
775         *outkey = *outval = NULL;
776         *outkeylen = *outvallen = 0;
777
778         YPLOCK();
779 again:
780         if (_yp_dobind(indomain, &ysd) != 0) {
781                 YPUNLOCK();
782                 return (YPERR_DOMAIN);
783         }
784
785         tv.tv_sec = _yplib_timeout;
786         tv.tv_usec = 0;
787
788         yprnk.domain = indomain;
789         yprnk.map = inmap;
790         bzero((char *)&yprkv, sizeof yprkv);
791
792         r = clnt_call(ysd->dom_client, YPPROC_FIRST,
793             (xdrproc_t)xdr_ypreq_nokey, &yprnk,
794             (xdrproc_t)xdr_ypresp_key_val, &yprkv, tv);
795         if (r != RPC_SUCCESS) {
796                 clnt_perror(ysd->dom_client, "yp_first: clnt_call");
797                 _yp_unbind(ysd);
798                 goto again;
799         }
800         if (!(r = ypprot_err(yprkv.stat))) {
801                 *outkeylen = yprkv.key.keydat_len;
802                 *outkey = (char *)malloc(*outkeylen+1);
803                 bcopy(yprkv.key.keydat_val, *outkey, *outkeylen);
804                 (*outkey)[*outkeylen] = '\0';
805                 *outvallen = yprkv.val.valdat_len;
806                 *outval = (char *)malloc(*outvallen+1);
807                 bcopy(yprkv.val.valdat_val, *outval, *outvallen);
808                 (*outval)[*outvallen] = '\0';
809         }
810
811         xdr_free((xdrproc_t)xdr_ypresp_key_val, &yprkv);
812         YPUNLOCK();
813         return (r);
814 }
815
816 int
817 yp_next(char *indomain, char *inmap, char *inkey, int inkeylen,
818     char **outkey, int *outkeylen, char **outval, int *outvallen)
819 {
820         struct ypresp_key_val yprkv;
821         struct ypreq_key yprk;
822         struct dom_binding *ysd;
823         struct timeval tv;
824         int r;
825
826         /* Sanity check */
827
828         if (inkey == NULL || !strlen(inkey) || inkeylen <= 0 ||
829             inmap == NULL || !strlen(inmap) ||
830             indomain == NULL || !strlen(indomain))
831                 return (YPERR_BADARGS);
832
833         *outkey = *outval = NULL;
834         *outkeylen = *outvallen = 0;
835
836         YPLOCK();
837 again:
838         if (_yp_dobind(indomain, &ysd) != 0) {
839                 YPUNLOCK();
840                 return (YPERR_DOMAIN);
841         }
842
843         tv.tv_sec = _yplib_timeout;
844         tv.tv_usec = 0;
845
846         yprk.domain = indomain;
847         yprk.map = inmap;
848         yprk.key.keydat_val = inkey;
849         yprk.key.keydat_len = inkeylen;
850         bzero((char *)&yprkv, sizeof yprkv);
851
852         r = clnt_call(ysd->dom_client, YPPROC_NEXT,
853             (xdrproc_t)xdr_ypreq_key, &yprk,
854             (xdrproc_t)xdr_ypresp_key_val, &yprkv, tv);
855         if (r != RPC_SUCCESS) {
856                 clnt_perror(ysd->dom_client, "yp_next: clnt_call");
857                 _yp_unbind(ysd);
858                 goto again;
859         }
860         if (!(r = ypprot_err(yprkv.stat))) {
861                 *outkeylen = yprkv.key.keydat_len;
862                 *outkey = (char *)malloc(*outkeylen+1);
863                 bcopy(yprkv.key.keydat_val, *outkey, *outkeylen);
864                 (*outkey)[*outkeylen] = '\0';
865                 *outvallen = yprkv.val.valdat_len;
866                 *outval = (char *)malloc(*outvallen+1);
867                 bcopy(yprkv.val.valdat_val, *outval, *outvallen);
868                 (*outval)[*outvallen] = '\0';
869         }
870
871         xdr_free((xdrproc_t)xdr_ypresp_key_val, &yprkv);
872         YPUNLOCK();
873         return (r);
874 }
875
876 int
877 yp_all(char *indomain, char *inmap, struct ypall_callback *incallback)
878 {
879         struct ypreq_nokey yprnk;
880         struct dom_binding *ysd;
881         struct timeval tv;
882         struct sockaddr_in clnt_sin;
883         CLIENT *clnt;
884         u_long status, savstat;
885         int clnt_sock;
886
887         /* Sanity check */
888
889         if (indomain == NULL || !strlen(indomain) ||
890             inmap == NULL || !strlen(inmap))
891                 return (YPERR_BADARGS);
892
893         YPLOCK();
894 again:
895
896         if (_yp_dobind(indomain, &ysd) != 0) {
897                 YPUNLOCK();
898                 return (YPERR_DOMAIN);
899         }
900
901         tv.tv_sec = _yplib_timeout;
902         tv.tv_usec = 0;
903
904         /* YPPROC_ALL manufactures its own channel to ypserv using TCP */
905
906         clnt_sock = RPC_ANYSOCK;
907         clnt_sin = ysd->dom_server_addr;
908         clnt_sin.sin_port = 0;
909         clnt = clnttcp_create(&clnt_sin, YPPROG, YPVERS, &clnt_sock, 0, 0);
910         if (clnt == NULL) {
911                 YPUNLOCK();
912                 printf("clnttcp_create failed\n");
913                 return (YPERR_PMAP);
914         }
915
916         yprnk.domain = indomain;
917         yprnk.map = inmap;
918         ypresp_allfn = incallback->foreach;
919         ypresp_data = (void *)incallback->data;
920
921         if (clnt_call(clnt, YPPROC_ALL,
922                 (xdrproc_t)xdr_ypreq_nokey, &yprnk,
923                 (xdrproc_t)xdr_ypresp_all_seq, &status, tv) != RPC_SUCCESS) {
924                         clnt_perror(ysd->dom_client, "yp_all: clnt_call");
925                         clnt_destroy(clnt);
926                         _yp_unbind(ysd);
927                         goto again;
928         }
929
930         clnt_destroy(clnt);
931         savstat = status;
932         xdr_free((xdrproc_t)xdr_ypresp_all_seq, &status);       /* not really needed... */
933         YPUNLOCK();
934         if (savstat != YP_NOMORE)
935                 return (ypprot_err(savstat));
936         return (0);
937 }
938
939 int
940 yp_order(char *indomain, char *inmap, int *outorder)
941 {
942         struct dom_binding *ysd;
943         struct ypresp_order ypro;
944         struct ypreq_nokey yprnk;
945         struct timeval tv;
946         int r;
947
948         /* Sanity check */
949
950         if (indomain == NULL || !strlen(indomain) ||
951             inmap == NULL || !strlen(inmap))
952                 return (YPERR_BADARGS);
953
954         YPLOCK();
955 again:
956         if (_yp_dobind(indomain, &ysd) != 0) {
957                 YPUNLOCK();
958                 return (YPERR_DOMAIN);
959         }
960
961         tv.tv_sec = _yplib_timeout;
962         tv.tv_usec = 0;
963
964         yprnk.domain = indomain;
965         yprnk.map = inmap;
966
967         bzero((char *)(char *)&ypro, sizeof ypro);
968
969         r = clnt_call(ysd->dom_client, YPPROC_ORDER,
970             (xdrproc_t)xdr_ypreq_nokey, &yprnk,
971             (xdrproc_t)xdr_ypresp_order, &ypro, tv);
972
973         /*
974          * NIS+ in YP compat mode doesn't support the YPPROC_ORDER
975          * procedure.
976          */
977         if (r == RPC_PROCUNAVAIL) {
978                 YPUNLOCK();
979                 return(YPERR_YPERR);
980         }
981
982         if (r != RPC_SUCCESS) {
983                 clnt_perror(ysd->dom_client, "yp_order: clnt_call");
984                 _yp_unbind(ysd);
985                 goto again;
986         }
987
988         if (!(r = ypprot_err(ypro.stat))) {
989                 *outorder = ypro.ordernum;
990         }
991
992         xdr_free((xdrproc_t)xdr_ypresp_order, &ypro);
993         YPUNLOCK();
994         return (r);
995 }
996
997 int
998 yp_master(char *indomain, char *inmap, char **outname)
999 {
1000         struct dom_binding *ysd;
1001         struct ypresp_master yprm;
1002         struct ypreq_nokey yprnk;
1003         struct timeval tv;
1004         int r;
1005
1006         /* Sanity check */
1007
1008         if (indomain == NULL || !strlen(indomain) ||
1009             inmap == NULL || !strlen(inmap))
1010                 return (YPERR_BADARGS);
1011         YPLOCK();
1012 again:
1013         if (_yp_dobind(indomain, &ysd) != 0) {
1014                 YPUNLOCK();
1015                 return (YPERR_DOMAIN);
1016         }
1017
1018         tv.tv_sec = _yplib_timeout;
1019         tv.tv_usec = 0;
1020
1021         yprnk.domain = indomain;
1022         yprnk.map = inmap;
1023
1024         bzero((char *)&yprm, sizeof yprm);
1025
1026         r = clnt_call(ysd->dom_client, YPPROC_MASTER,
1027             (xdrproc_t)xdr_ypreq_nokey, &yprnk,
1028             (xdrproc_t)xdr_ypresp_master, &yprm, tv);
1029         if (r != RPC_SUCCESS) {
1030                 clnt_perror(ysd->dom_client, "yp_master: clnt_call");
1031                 _yp_unbind(ysd);
1032                 goto again;
1033         }
1034
1035         if (!(r = ypprot_err(yprm.stat))) {
1036                 *outname = (char *)strdup(yprm.peer);
1037         }
1038
1039         xdr_free((xdrproc_t)xdr_ypresp_master, &yprm);
1040         YPUNLOCK();
1041         return (r);
1042 }
1043
1044 int
1045 yp_maplist(char *indomain, struct ypmaplist **outmaplist)
1046 {
1047         struct dom_binding *ysd;
1048         struct ypresp_maplist ypml;
1049         struct timeval tv;
1050         int r;
1051
1052         /* Sanity check */
1053
1054         if (indomain == NULL || !strlen(indomain))
1055                 return (YPERR_BADARGS);
1056
1057         YPLOCK();
1058 again:
1059         if (_yp_dobind(indomain, &ysd) != 0) {
1060                 YPUNLOCK();
1061                 return (YPERR_DOMAIN);
1062         }
1063
1064         tv.tv_sec = _yplib_timeout;
1065         tv.tv_usec = 0;
1066
1067         bzero((char *)&ypml, sizeof ypml);
1068
1069         r = clnt_call(ysd->dom_client, YPPROC_MAPLIST,
1070             (xdrproc_t)xdr_domainname, &indomain,
1071             (xdrproc_t)xdr_ypresp_maplist, &ypml, tv);
1072         if (r != RPC_SUCCESS) {
1073                 clnt_perror(ysd->dom_client, "yp_maplist: clnt_call");
1074                 _yp_unbind(ysd);
1075                 goto again;
1076         }
1077         if (!(r = ypprot_err(ypml.stat))) {
1078                 *outmaplist = ypml.maps;
1079         }
1080
1081         /* NO: xdr_free((xdrproc_t)xdr_ypresp_maplist, &ypml);*/
1082         YPUNLOCK();
1083         return (r);
1084 }
1085
1086 char *
1087 yperr_string(int incode)
1088 {
1089         const char *errstr;
1090         static char err[80];
1091
1092         switch (incode) {
1093         case 0:
1094                 errstr = "Success";
1095                 break;
1096         case YPERR_BADARGS:
1097                 errstr = "Request arguments bad";
1098                 break;
1099         case YPERR_RPC:
1100                 errstr = "RPC failure";
1101                 break;
1102         case YPERR_DOMAIN:
1103                 errstr = "Can't bind to server which serves this domain";
1104                 break;
1105         case YPERR_MAP:
1106                 errstr = "No such map in server's domain";
1107                 break;
1108         case YPERR_KEY:
1109                 errstr = "No such key in map";
1110                 break;
1111         case YPERR_YPERR:
1112                 errstr = "YP server error";
1113                 break;
1114         case YPERR_RESRC:
1115                 errstr = "Local resource allocation failure";
1116                 break;
1117         case YPERR_NOMORE:
1118                 errstr = "No more records in map database";
1119                 break;
1120         case YPERR_PMAP:
1121                 errstr = "Can't communicate with portmapper";
1122                 break;
1123         case YPERR_YPBIND:
1124                 errstr = "Can't communicate with ypbind";
1125                 break;
1126         case YPERR_YPSERV:
1127                 errstr = "Can't communicate with ypserv";
1128                 break;
1129         case YPERR_NODOM:
1130                 errstr = "Local domain name not set";
1131                 break;
1132         case YPERR_BADDB:
1133                 errstr = "Server data base is bad";
1134                 break;
1135         case YPERR_VERS:
1136                 errstr = "YP server version mismatch - server can't supply service.";
1137                 break;
1138         case YPERR_ACCESS:
1139                 errstr = "Access violation";
1140                 break;
1141         case YPERR_BUSY:
1142                 errstr = "Database is busy";
1143                 break;
1144         default:
1145                 errstr = NULL;
1146                 break;
1147         }
1148         if (errstr != NULL)
1149                 strlcpy(err, errstr, sizeof(err));
1150         else
1151                 snprintf(err, sizeof(err), "YP unknown error %d\n", incode);
1152         return (err);
1153 }
1154
1155 int
1156 ypprot_err(unsigned int incode)
1157 {
1158         switch (incode) {
1159         case YP_TRUE:
1160                 return (0);
1161         case YP_FALSE:
1162                 return (YPERR_YPBIND);
1163         case YP_NOMORE:
1164                 return (YPERR_NOMORE);
1165         case YP_NOMAP:
1166                 return (YPERR_MAP);
1167         case YP_NODOM:
1168                 return (YPERR_DOMAIN);
1169         case YP_NOKEY:
1170                 return (YPERR_KEY);
1171         case YP_BADOP:
1172                 return (YPERR_YPERR);
1173         case YP_BADDB:
1174                 return (YPERR_BADDB);
1175         case YP_YPERR:
1176                 return (YPERR_YPERR);
1177         case YP_BADARGS:
1178                 return (YPERR_BADARGS);
1179         case YP_VERS:
1180                 return (YPERR_VERS);
1181         }
1182         return (YPERR_YPERR);
1183 }
1184
1185 int
1186 _yp_check(char **dom)
1187 {
1188         char *unused;
1189
1190         YPLOCK();
1191         if (_yp_domain[0]=='\0')
1192                 if (yp_get_default_domain_locked(&unused)) {
1193                         YPUNLOCK();
1194                         return (0);
1195                 }
1196
1197         if (dom)
1198                 *dom = _yp_domain;
1199
1200         if (yp_bind_locked(_yp_domain) == 0) {
1201                 yp_unbind_locked(_yp_domain);
1202                 YPUNLOCK();
1203                 return (1);
1204         }
1205         YPUNLOCK();
1206         return (0);
1207 }