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