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