yplib.c revision 1.20 1 /* $NetBSD: yplib.c,v 1.20 1996/05/14 23:37:33 jtc Exp $ */
2
3 /*
4 * Copyright (c) 1992, 1993 Theo de Raadt <deraadt (at) fsa.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Theo de Raadt.
18 * 4. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
22 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #if defined(LIBC_SCCS) && !defined(lint)
35 static char rcsid[] = "$NetBSD: yplib.c,v 1.20 1996/05/14 23:37:33 jtc Exp $";
36 #endif
37
38 #include <sys/param.h>
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <sys/file.h>
42 #include <sys/uio.h>
43 #include <errno.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <rpc/rpc.h>
49 #include <rpc/xdr.h>
50 #include <rpcsvc/yp_prot.h>
51 #include <rpcsvc/ypclnt.h>
52
53 #define BINDINGDIR "/var/yp/binding"
54 #define YPBINDLOCK "/var/run/ypbind.lock"
55 #define YPMATCHCACHE
56
57 struct dom_binding *_ypbindlist;
58 static char _yp_domain[MAXHOSTNAMELEN];
59 int _yplib_timeout = 10;
60
61 static bool_t ypmatch_add __P((const char *, const char *, int, char *, int));
62 static bool_t ypmatch_find __P((const char *, const char *, int, const char **,
63 int *));
64 void _yp_unbind __P((struct dom_binding *));
65
66 #ifdef YPMATCHCACHE
67 int _yplib_cache = 5;
68
69 static struct ypmatch_ent {
70 struct ypmatch_ent *next;
71 char *map, *key;
72 char *val;
73 int keylen, vallen;
74 time_t expire_t;
75 } *ypmc;
76
77 static bool_t
78 ypmatch_add(map, key, keylen, val, vallen)
79 const char *map;
80 const char *key;
81 int keylen;
82 char *val;
83 int vallen;
84 {
85 struct ypmatch_ent *ep;
86 time_t t;
87
88 (void)time(&t);
89
90 for (ep = ypmc; ep; ep = ep->next)
91 if (ep->expire_t < t)
92 break;
93 if (ep == NULL) {
94 if ((ep = malloc(sizeof *ep)) == NULL)
95 return 0;
96 (void)memset(ep, 0, sizeof *ep);
97 if (ypmc)
98 ep->next = ypmc;
99 ypmc = ep;
100 }
101
102 if (ep->key) {
103 free(ep->key);
104 ep->key = NULL;
105 }
106 if (ep->val) {
107 free(ep->val);
108 ep->val = NULL;
109 }
110
111 if ((ep->key = malloc(keylen)) == NULL)
112 return 0;
113
114 if ((ep->val = malloc(vallen)) == NULL) {
115 free(ep->key);
116 ep->key = NULL;
117 return 0;
118 }
119
120 ep->keylen = keylen;
121 ep->vallen = vallen;
122
123 (void)memcpy(ep->key, key, ep->keylen);
124 (void)memcpy(ep->val, val, ep->vallen);
125
126 if (ep->map) {
127 if (strcmp(ep->map, map)) {
128 free(ep->map);
129 if ((ep->map = strdup(map)) == NULL)
130 return 0;
131 }
132 } else {
133 if ((ep->map = strdup(map)) == NULL)
134 return 0;
135 }
136
137 ep->expire_t = t + _yplib_cache;
138 return 1;
139 }
140
141 static bool_t
142 ypmatch_find(map, key, keylen, val, vallen)
143 const char *map;
144 const char *key;
145 int keylen;
146 const char **val;
147 int *vallen;
148 {
149 struct ypmatch_ent *ep;
150 time_t t;
151
152 if (ypmc == NULL)
153 return 0;
154
155 (void) time(&t);
156
157 for (ep = ypmc; ep; ep = ep->next) {
158 if (ep->keylen != keylen)
159 continue;
160 if (strcmp(ep->map, map))
161 continue;
162 if (memcmp(ep->key, key, keylen))
163 continue;
164 if (t > ep->expire_t)
165 continue;
166
167 *val = ep->val;
168 *vallen = ep->vallen;
169 return 1;
170 }
171 return 0;
172 }
173 #endif
174
175 int
176 _yp_dobind(dom, ypdb)
177 const char *dom;
178 struct dom_binding **ypdb;
179 {
180 static int pid = -1;
181 char path[MAXPATHLEN];
182 struct dom_binding *ysd, *ysd2;
183 struct ypbind_resp ypbr;
184 struct timeval tv;
185 struct sockaddr_in clnt_sin;
186 int clnt_sock, fd, gpid;
187 CLIENT *client;
188 int new = 0, r;
189 int count = 0;
190
191 /*
192 * test if YP is running or not
193 */
194 if ((fd = open(YPBINDLOCK, O_RDONLY)) == -1)
195 return YPERR_YPBIND;
196 if (!(flock(fd, LOCK_EX | LOCK_NB) == -1 && errno == EWOULDBLOCK)) {
197 (void)close(fd);
198 return YPERR_YPBIND;
199 }
200 (void)close(fd);
201
202 gpid = getpid();
203 if (!(pid == -1 || pid == gpid)) {
204 ysd = _ypbindlist;
205 while (ysd) {
206 if (ysd->dom_client)
207 clnt_destroy(ysd->dom_client);
208 ysd2 = ysd->dom_pnext;
209 free(ysd);
210 ysd = ysd2;
211 }
212 _ypbindlist = NULL;
213 }
214 pid = gpid;
215
216 if (ypdb != NULL)
217 *ypdb = NULL;
218
219 if (dom == NULL || strlen(dom) == 0)
220 return YPERR_BADARGS;
221
222 for (ysd = _ypbindlist; ysd; ysd = ysd->dom_pnext)
223 if (strcmp(dom, ysd->dom_domain) == 0)
224 break;
225 if (ysd == NULL) {
226 if ((ysd = malloc(sizeof *ysd)) == NULL)
227 return YPERR_YPERR;
228 (void)memset(ysd, 0, sizeof *ysd);
229 ysd->dom_socket = -1;
230 ysd->dom_vers = 0;
231 new = 1;
232 }
233 again:
234 if (ysd->dom_vers == 0) {
235 (void) snprintf(path, sizeof(path), "%s/%s.%d",
236 BINDINGDIR, dom, 2);
237 if ((fd = open(path, O_RDONLY)) == -1) {
238 /*
239 * no binding file, YP is dead, or not yet fully
240 * alive.
241 */
242 goto trynet;
243 }
244 if (flock(fd, LOCK_EX | LOCK_NB) == -1 &&
245 errno == EWOULDBLOCK) {
246 struct iovec iov[2];
247 struct ypbind_resp ybr;
248 u_short ypb_port;
249 struct ypbind_binding *bn;
250
251 iov[0].iov_base = (caddr_t) & ypb_port;
252 iov[0].iov_len = sizeof ypb_port;
253 iov[1].iov_base = (caddr_t) & ybr;
254 iov[1].iov_len = sizeof ybr;
255
256 r = readv(fd, iov, 2);
257 if (r != iov[0].iov_len + iov[1].iov_len) {
258 (void)close(fd);
259 ysd->dom_vers = -1;
260 goto again;
261 }
262 (void)memset(&ysd->dom_server_addr, 0,
263 sizeof ysd->dom_server_addr);
264 ysd->dom_server_addr.sin_len =
265 sizeof(struct sockaddr_in);
266 ysd->dom_server_addr.sin_family = AF_INET;
267 bn = &ybr.ypbind_respbody.ypbind_bindinfo;
268 ysd->dom_server_addr.sin_port =
269 bn->ypbind_binding_port;
270
271 ysd->dom_server_addr.sin_addr =
272 bn->ypbind_binding_addr;
273
274 ysd->dom_server_port = ysd->dom_server_addr.sin_port;
275 (void)close(fd);
276 goto gotit;
277 } else {
278 /* no lock on binding file, YP is dead. */
279 (void)close(fd);
280 if (new)
281 free(ysd);
282 return YPERR_YPBIND;
283 }
284 }
285 trynet:
286 if (ysd->dom_vers == -1 || ysd->dom_vers == 0) {
287 struct ypbind_binding *bn;
288 (void)memset(&clnt_sin, 0, sizeof clnt_sin);
289 clnt_sin.sin_len = sizeof(struct sockaddr_in);
290 clnt_sin.sin_family = AF_INET;
291 clnt_sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
292
293 clnt_sock = RPC_ANYSOCK;
294 client = clnttcp_create(&clnt_sin, YPBINDPROG, YPBINDVERS,
295 &clnt_sock, 0, 0);
296 if (client == NULL) {
297 clnt_pcreateerror("clnttcp_create");
298 if (new)
299 free(ysd);
300 return YPERR_YPBIND;
301 }
302 tv.tv_sec = _yplib_timeout;
303 tv.tv_usec = 0;
304 r = clnt_call(client, YPBINDPROC_DOMAIN, xdr_domainname,
305 dom, xdr_ypbind_resp, &ypbr, tv);
306 if (r != RPC_SUCCESS) {
307 if (new == 0 || count)
308 fprintf(stderr,
309 "YP server for domain %s not responding, still trying\n",
310 dom);
311 count++;
312 clnt_destroy(client);
313 ysd->dom_vers = -1;
314 goto again;
315 }
316 clnt_destroy(client);
317
318 (void)memset(&ysd->dom_server_addr, 0,
319 sizeof ysd->dom_server_addr);
320 ysd->dom_server_addr.sin_len = sizeof(struct sockaddr_in);
321 ysd->dom_server_addr.sin_family = AF_INET;
322 bn = &ypbr.ypbind_respbody.ypbind_bindinfo;
323 ysd->dom_server_addr.sin_port =
324 bn->ypbind_binding_port;
325 ysd->dom_server_addr.sin_addr.s_addr =
326 bn->ypbind_binding_addr.s_addr;
327 ysd->dom_server_port =
328 bn->ypbind_binding_port;
329 gotit:
330 ysd->dom_vers = YPVERS;
331 (void)strcpy(ysd->dom_domain, dom);
332 }
333 tv.tv_sec = _yplib_timeout / 2;
334 tv.tv_usec = 0;
335 if (ysd->dom_client)
336 clnt_destroy(ysd->dom_client);
337 ysd->dom_socket = RPC_ANYSOCK;
338 ysd->dom_client = clntudp_create(&ysd->dom_server_addr,
339 YPPROG, YPVERS, tv, &ysd->dom_socket);
340 if (ysd->dom_client == NULL) {
341 clnt_pcreateerror("clntudp_create");
342 ysd->dom_vers = -1;
343 goto again;
344 }
345 if (fcntl(ysd->dom_socket, F_SETFD, 1) == -1)
346 perror("fcntl: F_SETFD");
347
348 if (new) {
349 ysd->dom_pnext = _ypbindlist;
350 _ypbindlist = ysd;
351 }
352 if (ypdb != NULL)
353 *ypdb = ysd;
354 return 0;
355 }
356
357 void
358 _yp_unbind(ypb)
359 struct dom_binding *ypb;
360 {
361 clnt_destroy(ypb->dom_client);
362 ypb->dom_client = NULL;
363 ypb->dom_socket = -1;
364 }
365
366 int
367 yp_bind(dom)
368 const char *dom;
369 {
370 return _yp_dobind(dom, NULL);
371 }
372
373 void
374 yp_unbind(dom)
375 const char *dom;
376 {
377 struct dom_binding *ypb, *ypbp;
378
379 ypbp = NULL;
380 for (ypb = _ypbindlist; ypb; ypb = ypb->dom_pnext) {
381 if (strcmp(dom, ypb->dom_domain) == 0) {
382 clnt_destroy(ypb->dom_client);
383 if (ypbp)
384 ypbp->dom_pnext = ypb->dom_pnext;
385 else
386 _ypbindlist = ypb->dom_pnext;
387 free(ypb);
388 return;
389 }
390 ypbp = ypb;
391 }
392 return;
393 }
394
395 int
396 yp_match(indomain, inmap, inkey, inkeylen, outval, outvallen)
397 const char *indomain;
398 const char *inmap;
399 const char *inkey;
400 int inkeylen;
401 char **outval;
402 int *outvallen;
403 {
404 struct dom_binding *ysd;
405 struct ypresp_val yprv;
406 struct timeval tv;
407 struct ypreq_key yprk;
408 int r;
409
410 *outval = NULL;
411 *outvallen = 0;
412
413 again:
414 if (_yp_dobind(indomain, &ysd) != 0)
415 return YPERR_DOMAIN;
416
417 #ifdef YPMATCHCACHE
418 if (!strcmp(_yp_domain, indomain) && ypmatch_find(inmap, inkey,
419 inkeylen, &yprv.valdat.dptr, &yprv.valdat.dsize)) {
420 *outvallen = yprv.valdat.dsize;
421 if ((*outval = malloc(*outvallen + 1)) == NULL)
422 return YPERR_YPERR;
423 (void)memcpy(*outval, yprv.valdat.dptr, *outvallen);
424 (*outval)[*outvallen] = '\0';
425 return 0;
426 }
427 #endif
428
429 tv.tv_sec = _yplib_timeout;
430 tv.tv_usec = 0;
431
432 yprk.domain = indomain;
433 yprk.map = inmap;
434 yprk.keydat.dptr = (char *) inkey;
435 yprk.keydat.dsize = inkeylen;
436
437 memset(&yprv, 0, sizeof yprv);
438
439 r = clnt_call(ysd->dom_client, YPPROC_MATCH,
440 xdr_ypreq_key, &yprk, xdr_ypresp_val, &yprv, tv);
441 if (r != RPC_SUCCESS) {
442 clnt_perror(ysd->dom_client, "yp_match: clnt_call");
443 ysd->dom_vers = -1;
444 goto again;
445 }
446 if (!(r = ypprot_err(yprv.status))) {
447 *outvallen = yprv.valdat.dsize;
448 if ((*outval = malloc(*outvallen + 1)) == NULL)
449 return YPERR_YPERR;
450 (void)memcpy(*outval, yprv.valdat.dptr, *outvallen);
451 (*outval)[*outvallen] = '\0';
452 #ifdef YPMATCHCACHE
453 if (strcmp(_yp_domain, indomain) == 0)
454 if (!ypmatch_add(inmap, inkey, inkeylen,
455 *outval, *outvallen))
456 r = RPC_SYSTEMERROR;
457 #endif
458 }
459 xdr_free(xdr_ypresp_val, (char *) &yprv);
460 _yp_unbind(ysd);
461 return r;
462 }
463
464 int
465 yp_get_default_domain(domp)
466 char **domp;
467 {
468 *domp = NULL;
469 if (_yp_domain[0] == '\0')
470 if (getdomainname(_yp_domain, sizeof _yp_domain))
471 return YPERR_NODOM;
472 *domp = _yp_domain;
473 return 0;
474 }
475
476 int
477 yp_first(indomain, inmap, outkey, outkeylen, outval, outvallen)
478 const char *indomain;
479 const char *inmap;
480 char **outkey;
481 int *outkeylen;
482 char **outval;
483 int *outvallen;
484 {
485 struct ypresp_key_val yprkv;
486 struct ypreq_nokey yprnk;
487 struct dom_binding *ysd;
488 struct timeval tv;
489 int r;
490
491 *outkey = *outval = NULL;
492 *outkeylen = *outvallen = 0;
493
494 again:
495 if (_yp_dobind(indomain, &ysd) != 0)
496 return YPERR_DOMAIN;
497
498 tv.tv_sec = _yplib_timeout;
499 tv.tv_usec = 0;
500
501 yprnk.domain = indomain;
502 yprnk.map = inmap;
503 (void)memset(&yprkv, 0, sizeof yprkv);
504
505 r = clnt_call(ysd->dom_client, YPPROC_FIRST,
506 xdr_ypreq_nokey, &yprnk, xdr_ypresp_key_val, &yprkv, tv);
507 if (r != RPC_SUCCESS) {
508 clnt_perror(ysd->dom_client, "yp_first: clnt_call");
509 ysd->dom_vers = -1;
510 goto again;
511 }
512 if (!(r = ypprot_err(yprkv.status))) {
513 *outkeylen = yprkv.keydat.dsize;
514 if ((*outkey = malloc(*outkeylen + 1)) == NULL)
515 r = RPC_SYSTEMERROR;
516 else {
517 (void)memcpy(*outkey, yprkv.keydat.dptr, *outkeylen);
518 (*outkey)[*outkeylen] = '\0';
519 }
520 *outvallen = yprkv.valdat.dsize;
521 if ((*outval = malloc(*outvallen + 1)) == NULL)
522 r = RPC_SYSTEMERROR;
523 else {
524 (void)memcpy(*outval, yprkv.valdat.dptr, *outvallen);
525 (*outval)[*outvallen] = '\0';
526 }
527 }
528 xdr_free(xdr_ypresp_key_val, (char *) &yprkv);
529 _yp_unbind(ysd);
530 return r;
531 }
532
533 int
534 yp_next(indomain, inmap, inkey, inkeylen, outkey, outkeylen, outval, outvallen)
535 const char *indomain;
536 const char *inmap;
537 const char *inkey;
538 int inkeylen;
539 char **outkey;
540 int *outkeylen;
541 char **outval;
542 int *outvallen;
543 {
544 struct ypresp_key_val yprkv;
545 struct ypreq_key yprk;
546 struct dom_binding *ysd;
547 struct timeval tv;
548 int r;
549
550 *outkey = *outval = NULL;
551 *outkeylen = *outvallen = 0;
552
553 again:
554 if (_yp_dobind(indomain, &ysd) != 0)
555 return YPERR_DOMAIN;
556
557 tv.tv_sec = _yplib_timeout;
558 tv.tv_usec = 0;
559
560 yprk.domain = indomain;
561 yprk.map = inmap;
562 yprk.keydat.dptr = inkey;
563 yprk.keydat.dsize = inkeylen;
564 (void)memset(&yprkv, 0, sizeof yprkv);
565
566 r = clnt_call(ysd->dom_client, YPPROC_NEXT,
567 xdr_ypreq_key, &yprk, xdr_ypresp_key_val, &yprkv, tv);
568 if (r != RPC_SUCCESS) {
569 clnt_perror(ysd->dom_client, "yp_next: clnt_call");
570 ysd->dom_vers = -1;
571 goto again;
572 }
573 if (!(r = ypprot_err(yprkv.status))) {
574 *outkeylen = yprkv.keydat.dsize;
575 if ((*outkey = malloc(*outkeylen + 1)) == NULL)
576 r = RPC_SYSTEMERROR;
577 else {
578 (void)memcpy(*outkey, yprkv.keydat.dptr, *outkeylen);
579 (*outkey)[*outkeylen] = '\0';
580 }
581 *outvallen = yprkv.valdat.dsize;
582 if ((*outval = malloc(*outvallen + 1)) == NULL)
583 r = RPC_SYSTEMERROR;
584 else {
585 (void)memcpy(*outval, yprkv.valdat.dptr, *outvallen);
586 (*outval)[*outvallen] = '\0';
587 }
588 }
589 xdr_free(xdr_ypresp_key_val, (char *) &yprkv);
590 _yp_unbind(ysd);
591 return r;
592 }
593
594 int
595 yp_maplist(indomain, outmaplist)
596 const char *indomain;
597 struct ypmaplist **outmaplist;
598 {
599 struct dom_binding *ysd;
600 struct ypresp_maplist ypml;
601 struct timeval tv;
602 int r;
603
604 again:
605 if (_yp_dobind(indomain, &ysd) != 0)
606 return YPERR_DOMAIN;
607
608 tv.tv_sec = _yplib_timeout;
609 tv.tv_usec = 0;
610
611 memset(&ypml, 0, sizeof ypml);
612
613 r = clnt_call(ysd->dom_client, YPPROC_MAPLIST,
614 xdr_domainname, indomain, xdr_ypresp_maplist, &ypml, tv);
615 if (r != RPC_SUCCESS) {
616 clnt_perror(ysd->dom_client, "yp_maplist: clnt_call");
617 ysd->dom_vers = -1;
618 goto again;
619 }
620 *outmaplist = ypml.list;
621 /* NO: xdr_free(xdr_ypresp_maplist, &ypml); */
622 _yp_unbind(ysd);
623 return ypprot_err(ypml.status);
624 }
625
626 int
627 ypprot_err(incode)
628 unsigned int incode;
629 {
630 switch (incode) {
631 case YP_TRUE:
632 return 0;
633 case YP_FALSE:
634 return YPERR_YPBIND;
635 case YP_NOMORE:
636 return YPERR_NOMORE;
637 case YP_NOMAP:
638 return YPERR_MAP;
639 case YP_NODOM:
640 return YPERR_NODOM;
641 case YP_NOKEY:
642 return YPERR_KEY;
643 case YP_BADOP:
644 return YPERR_YPERR;
645 case YP_BADDB:
646 return YPERR_BADDB;
647 case YP_YPERR:
648 return YPERR_YPERR;
649 case YP_BADARGS:
650 return YPERR_BADARGS;
651 case YP_VERS:
652 return YPERR_VERS;
653 }
654 return YPERR_YPERR;
655 }
656
657 int
658 _yp_check(dom)
659 char **dom;
660 {
661 char *unused;
662
663 if (_yp_domain[0] == '\0')
664 if (yp_get_default_domain(&unused))
665 return 0;
666
667 if (dom)
668 *dom = _yp_domain;
669
670 if (yp_bind(_yp_domain) == 0)
671 return 1;
672 return 0;
673 }
674