xref: /netbsd-src/lib/libc/yp/yplib.c (revision fdecd6a253f999ae92b139670d9e15cc9df4497c)
1 /*	$NetBSD: yplib.c,v 1.28 1997/07/07 02:00:43 lukem Exp $	 */
2 
3 /*
4  * Copyright (c) 1992, 1993 Theo de Raadt <deraadt@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.28 1997/07/07 02:00:43 lukem 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 <arpa/nameser.h>
44 #include <errno.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include <rpc/rpc.h>
50 #include <rpc/xdr.h>
51 #include <rpcsvc/yp_prot.h>
52 #include <rpcsvc/ypclnt.h>
53 
54 #define BINDINGDIR	"/var/yp/binding"
55 #define YPBINDLOCK	"/var/run/ypbind.lock"
56 
57 struct dom_binding *_ypbindlist;
58 char _yp_domain[MAXHOSTNAMELEN];
59 
60 #define YPLIB_TIMEOUT		10
61 #define YPLIB_RPC_RETRIES	4
62 
63 struct timeval _yplib_timeout = { YPLIB_TIMEOUT, 0 };
64 struct timeval _yplib_rpc_timeout = { YPLIB_TIMEOUT / YPLIB_RPC_RETRIES,
65 	1000000 * (YPLIB_TIMEOUT % YPLIB_RPC_RETRIES) / YPLIB_RPC_RETRIES };
66 int _yplib_nerrs = 5;
67 
68 void	_yp_unbind __P((struct dom_binding *));
69 int	_yp_invalid_domain __P((const char *));
70 
71 int
72 _yp_dobind(dom, ypdb)
73 	const char     *dom;
74 	struct dom_binding **ypdb;
75 {
76 	static int      pid = -1;
77 	char            path[MAXPATHLEN];
78 	struct dom_binding *ysd, *ysd2;
79 	struct ypbind_resp ypbr;
80 	struct sockaddr_in clnt_sin;
81 	int             clnt_sock, fd, gpid;
82 	CLIENT         *client;
83 	int             new = 0, r;
84 	int             nerrs = 0;
85 
86 	if (dom == NULL || *dom == '\0')
87 		return YPERR_BADARGS;
88 
89 	/*
90 	 * test if YP is running or not
91 	 */
92 	if ((fd = open(YPBINDLOCK, O_RDONLY)) == -1)
93 		return YPERR_YPBIND;
94 	if (!(flock(fd, LOCK_EX | LOCK_NB) == -1 && errno == EWOULDBLOCK)) {
95 		(void)close(fd);
96 		return YPERR_YPBIND;
97 	}
98 	(void)close(fd);
99 
100 	gpid = getpid();
101 	if (!(pid == -1 || pid == gpid)) {
102 		ysd = _ypbindlist;
103 		while (ysd) {
104 			if (ysd->dom_client)
105 				clnt_destroy(ysd->dom_client);
106 			ysd2 = ysd->dom_pnext;
107 			free(ysd);
108 			ysd = ysd2;
109 		}
110 		_ypbindlist = NULL;
111 	}
112 	pid = gpid;
113 
114 	if (ypdb != NULL)
115 		*ypdb = NULL;
116 
117 	for (ysd = _ypbindlist; ysd; ysd = ysd->dom_pnext)
118 		if (strcmp(dom, ysd->dom_domain) == 0)
119 			break;
120 	if (ysd == NULL) {
121 		if ((ysd = malloc(sizeof *ysd)) == NULL)
122 			return YPERR_YPERR;
123 		(void)memset(ysd, 0, sizeof *ysd);
124 		ysd->dom_socket = -1;
125 		ysd->dom_vers = 0;
126 		new = 1;
127 	}
128 again:
129 	if (ysd->dom_vers == 0) {
130 		(void) snprintf(path, sizeof(path), "%s/%s.%d",
131 				BINDINGDIR, dom, 2);
132 		if ((fd = open(path, O_RDONLY)) == -1) {
133 			/*
134 			 * no binding file, YP is dead, or not yet fully
135 			 * alive.
136 			 */
137 			goto trynet;
138 		}
139 		if (flock(fd, LOCK_EX | LOCK_NB) == -1 &&
140 		    errno == EWOULDBLOCK) {
141 			struct iovec    iov[2];
142 			struct ypbind_resp ybr;
143 			u_short         ypb_port;
144 			struct ypbind_binding *bn;
145 
146 			iov[0].iov_base = (caddr_t) & ypb_port;
147 			iov[0].iov_len = sizeof ypb_port;
148 			iov[1].iov_base = (caddr_t) & ybr;
149 			iov[1].iov_len = sizeof ybr;
150 
151 			r = readv(fd, iov, 2);
152 			if (r != iov[0].iov_len + iov[1].iov_len) {
153 				(void)close(fd);
154 				ysd->dom_vers = -1;
155 				goto again;
156 			}
157 			(void)memset(&ysd->dom_server_addr, 0,
158 				     sizeof ysd->dom_server_addr);
159 			ysd->dom_server_addr.sin_len =
160 				sizeof(struct sockaddr_in);
161 			ysd->dom_server_addr.sin_family = AF_INET;
162 			bn = &ybr.ypbind_respbody.ypbind_bindinfo;
163 			ysd->dom_server_addr.sin_port =
164 				bn->ypbind_binding_port;
165 
166 			ysd->dom_server_addr.sin_addr =
167 				bn->ypbind_binding_addr;
168 
169 			ysd->dom_server_port = ysd->dom_server_addr.sin_port;
170 			(void)close(fd);
171 			goto gotit;
172 		} else {
173 			/* no lock on binding file, YP is dead. */
174 			(void)close(fd);
175 			if (new)
176 				free(ysd);
177 			return YPERR_YPBIND;
178 		}
179 	}
180 trynet:
181 	if (ysd->dom_vers == -1 || ysd->dom_vers == 0) {
182 		struct ypbind_binding *bn;
183 		(void)memset(&clnt_sin, 0, sizeof clnt_sin);
184 		clnt_sin.sin_len = sizeof(struct sockaddr_in);
185 		clnt_sin.sin_family = AF_INET;
186 		clnt_sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
187 
188 		clnt_sock = RPC_ANYSOCK;
189 		client = clnttcp_create(&clnt_sin, YPBINDPROG, YPBINDVERS,
190 					&clnt_sock, 0, 0);
191 		if (client == NULL) {
192 			clnt_pcreateerror("clnttcp_create");
193 			if (new)
194 				free(ysd);
195 			return YPERR_YPBIND;
196 		}
197 		r = clnt_call(client, YPBINDPROC_DOMAIN,
198 		    xdr_ypdomain_wrap_string, &dom, xdr_ypbind_resp,
199 		    &ypbr, _yplib_timeout);
200 		if (r != RPC_SUCCESS) {
201 			if (new == 0 && ++nerrs == _yplib_nerrs) {
202 				nerrs = 0;
203 				fprintf(stderr,
204 		    "YP server for domain %s not responding, still trying\n",
205 				    dom);
206 			}
207 			clnt_destroy(client);
208 			ysd->dom_vers = -1;
209 			goto again;
210 		}
211 		clnt_destroy(client);
212 
213 		(void)memset(&ysd->dom_server_addr, 0,
214 			     sizeof ysd->dom_server_addr);
215 		ysd->dom_server_addr.sin_len = sizeof(struct sockaddr_in);
216 		ysd->dom_server_addr.sin_family = AF_INET;
217 		bn = &ypbr.ypbind_respbody.ypbind_bindinfo;
218 		ysd->dom_server_addr.sin_port =
219 			bn->ypbind_binding_port;
220 		ysd->dom_server_addr.sin_addr.s_addr =
221 			bn->ypbind_binding_addr.s_addr;
222 		ysd->dom_server_port =
223 			bn->ypbind_binding_port;
224 gotit:
225 		ysd->dom_vers = YPVERS;
226 		(void)strncpy(ysd->dom_domain, dom, sizeof(ysd->dom_domain)-1);
227 	}
228 	if (ysd->dom_client)
229 		clnt_destroy(ysd->dom_client);
230 	ysd->dom_socket = RPC_ANYSOCK;
231 	ysd->dom_client = clntudp_create(&ysd->dom_server_addr,
232 	    YPPROG, YPVERS, _yplib_rpc_timeout, &ysd->dom_socket);
233 	if (ysd->dom_client == NULL) {
234 		clnt_pcreateerror("clntudp_create");
235 		ysd->dom_vers = -1;
236 		goto again;
237 	}
238 	if (fcntl(ysd->dom_socket, F_SETFD, 1) == -1)
239 		perror("fcntl: F_SETFD");
240 
241 	if (new) {
242 		ysd->dom_pnext = _ypbindlist;
243 		_ypbindlist = ysd;
244 	}
245 	if (ypdb != NULL)
246 		*ypdb = ysd;
247 	return 0;
248 }
249 
250 void
251 _yp_unbind(ypb)
252 	struct dom_binding *ypb;
253 {
254 	clnt_destroy(ypb->dom_client);
255 	ypb->dom_client = NULL;
256 	ypb->dom_socket = -1;
257 }
258 
259 int
260 yp_bind(dom)
261 	const char     *dom;
262 {
263 	if (_yp_invalid_domain(dom))
264 		return YPERR_BADARGS;
265 
266 	return _yp_dobind(dom, NULL);
267 }
268 
269 void
270 yp_unbind(dom)
271 	const char     *dom;
272 {
273 	struct dom_binding *ypb, *ypbp;
274 
275 	if (_yp_invalid_domain(dom))
276 		return;
277 
278 	ypbp = NULL;
279 	for (ypb = _ypbindlist; ypb; ypb = ypb->dom_pnext) {
280 		if (strcmp(dom, ypb->dom_domain) == 0) {
281 			clnt_destroy(ypb->dom_client);
282 			if (ypbp)
283 				ypbp->dom_pnext = ypb->dom_pnext;
284 			else
285 				_ypbindlist = ypb->dom_pnext;
286 			free(ypb);
287 			return;
288 		}
289 		ypbp = ypb;
290 	}
291 	return;
292 }
293 
294 int
295 yp_get_default_domain(domp)
296 	char          **domp;
297 {
298 	*domp = NULL;
299 	if (_yp_domain[0] == '\0')
300 		if (getdomainname(_yp_domain, sizeof _yp_domain))
301 			return YPERR_NODOM;
302 	*domp = _yp_domain;
303 	return 0;
304 }
305 
306 int
307 _yp_check(dom)
308 	char          **dom;
309 {
310 	char           *unused;
311 
312 	if (_yp_domain[0] == '\0')
313 		if (yp_get_default_domain(&unused))
314 			return 0;
315 
316 	if (dom)
317 		*dom = _yp_domain;
318 
319 	if (yp_bind(_yp_domain) == 0)
320 		return 1;
321 	return 0;
322 }
323 
324 /*
325  * _yp_invalid_domain: check if given domainname isn't RFC1035 compliant
326  * returns non-zero if invalid
327  */
328 int
329 _yp_invalid_domain(dom)
330 	const char *dom;
331 {
332 	const char	*p;
333 
334 	if (dom == NULL || *dom == '\0')
335 		return 1;
336 
337 #define	is_digit(x)	((x) >= '0' && (x) <= '9')
338 #define	is_letter(x)	(((x) >= 'a' && (x) <= 'z') || ((x) >='A' && (x) <='Z'))
339 
340 	for (p = dom; *p != '\0'; p++) {
341 		int len;
342 		if (!is_letter(*p))		/* label starts with a letter */
343 			return 1;
344 		p++;
345 		len = 0;			/* then has [-a-zA-Z0-9] */
346 		while (is_digit(*p) || is_letter(*p) || *p == '-') {
347 			p++;
348 			if (++len > MAXLABEL)
349 				return 1;	/* label is too long */
350 		}
351 		if (*(p-1) == '-')		/* no trailing - for label */
352 			return 1;
353 		if (*p == '\0')
354 			break;
355 		else if (*p != '.')
356 			return 1;
357 		else if (*(p+1) == '\0')	/* no trailing . for domain */
358 			return 1;
359 	}
360 	if ((p - dom) > YPMAXDOMAIN)
361 		return 1;
362 	return 0;
363 }
364