ypbind.c revision 1.97 1 1.97 dholland /* $NetBSD: ypbind.c,v 1.97 2014/06/10 17:19:36 dholland Exp $ */
2 1.20 cgd
3 1.2 deraadt /*
4 1.7 deraadt * Copyright (c) 1992, 1993 Theo de Raadt <deraadt (at) fsa.ca>
5 1.2 deraadt * All rights reserved.
6 1.2 deraadt *
7 1.2 deraadt * Redistribution and use in source and binary forms, with or without
8 1.2 deraadt * modification, are permitted provided that the following conditions
9 1.2 deraadt * are met:
10 1.2 deraadt * 1. Redistributions of source code must retain the above copyright
11 1.2 deraadt * notice, this list of conditions and the following disclaimer.
12 1.2 deraadt * 2. Redistributions in binary form must reproduce the above copyright
13 1.2 deraadt * notice, this list of conditions and the following disclaimer in the
14 1.2 deraadt * documentation and/or other materials provided with the distribution.
15 1.2 deraadt *
16 1.2 deraadt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
17 1.2 deraadt * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 1.2 deraadt * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 1.2 deraadt * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20 1.2 deraadt * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 1.2 deraadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 1.2 deraadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 1.2 deraadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 1.2 deraadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 1.2 deraadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 1.2 deraadt * SUCH DAMAGE.
27 1.2 deraadt */
28 1.2 deraadt
29 1.30 lukem #include <sys/cdefs.h>
30 1.2 deraadt #ifndef LINT
31 1.97 dholland __RCSID("$NetBSD: ypbind.c,v 1.97 2014/06/10 17:19:36 dholland Exp $");
32 1.2 deraadt #endif
33 1.2 deraadt
34 1.67 dholland #include <sys/types.h>
35 1.1 deraadt #include <sys/param.h>
36 1.67 dholland #include <sys/file.h>
37 1.1 deraadt #include <sys/ioctl.h>
38 1.1 deraadt #include <sys/signal.h>
39 1.1 deraadt #include <sys/socket.h>
40 1.67 dholland #include <sys/stat.h>
41 1.67 dholland #include <sys/syslog.h>
42 1.6 deraadt #include <sys/uio.h>
43 1.67 dholland #include <arpa/inet.h>
44 1.67 dholland #include <net/if.h>
45 1.67 dholland #include <ctype.h>
46 1.67 dholland #include <dirent.h>
47 1.67 dholland #include <err.h>
48 1.67 dholland #include <errno.h>
49 1.38 kleink #include <fcntl.h>
50 1.67 dholland #include <ifaddrs.h>
51 1.27 thorpej #include <limits.h>
52 1.67 dholland #include <netdb.h>
53 1.97 dholland #include <signal.h>
54 1.67 dholland #include <stdarg.h>
55 1.1 deraadt #include <stdio.h>
56 1.14 cgd #include <stdlib.h>
57 1.67 dholland #include <string.h>
58 1.37 bouyer #include <syslog.h>
59 1.67 dholland #include <unistd.h>
60 1.67 dholland #include <util.h>
61 1.67 dholland
62 1.1 deraadt #include <rpc/rpc.h>
63 1.1 deraadt #include <rpc/xdr.h>
64 1.1 deraadt #include <rpc/pmap_clnt.h>
65 1.1 deraadt #include <rpc/pmap_prot.h>
66 1.1 deraadt #include <rpc/pmap_rmt.h>
67 1.1 deraadt #include <rpcsvc/yp_prot.h>
68 1.1 deraadt #include <rpcsvc/ypclnt.h>
69 1.1 deraadt
70 1.27 thorpej #include "pathnames.h"
71 1.27 thorpej
72 1.68 dholland #define YPSERVERSSUFF ".ypservers"
73 1.68 dholland #define BINDINGDIR (_PATH_VAR_YP "binding")
74 1.68 dholland
75 1.24 christos #ifndef O_SHLOCK
76 1.24 christos #define O_SHLOCK 0
77 1.24 christos #endif
78 1.24 christos
79 1.68 dholland int _yp_invalid_domain(const char *); /* XXX libc internal */
80 1.68 dholland
81 1.68 dholland ////////////////////////////////////////////////////////////
82 1.68 dholland // types and globals
83 1.27 thorpej
84 1.68 dholland typedef enum {
85 1.81 dholland YPBIND_DIRECT, YPBIND_BROADCAST,
86 1.68 dholland } ypbind_mode_t;
87 1.1 deraadt
88 1.96 dholland enum domainstates {
89 1.96 dholland DOM_NEW, /* not yet bound */
90 1.96 dholland DOM_ALIVE, /* bound and healthy */
91 1.96 dholland DOM_PINGING, /* ping outstanding */
92 1.96 dholland DOM_LOST, /* binding timed out, looking for a new one */
93 1.96 dholland DOM_DEAD, /* long-term lost, in exponential backoff */
94 1.96 dholland };
95 1.96 dholland
96 1.75 dholland struct domain {
97 1.78 dholland struct domain *dom_next;
98 1.75 dholland
99 1.79 dholland char dom_name[YPMAXDOMAIN + 1];
100 1.1 deraadt struct sockaddr_in dom_server_addr;
101 1.35 lukem long dom_vers;
102 1.96 dholland time_t dom_checktime; /* time of next check/contact */
103 1.96 dholland time_t dom_asktime; /* time we were last DOMAIN'd */
104 1.96 dholland time_t dom_losttime; /* time the binding was lost, or 0 */
105 1.96 dholland unsigned dom_backofftime; /* current backoff period, when DEAD */
106 1.1 deraadt int dom_lockfd;
107 1.96 dholland enum domainstates dom_state;
108 1.64 dholland uint32_t dom_xid;
109 1.84 dholland FILE *dom_serversfile; /* /var/yp/binding/foo.ypservers */
110 1.84 dholland int dom_been_ypset; /* ypset been done on this domain? */
111 1.83 dholland ypbind_mode_t dom_ypbindmode; /* broadcast or direct */
112 1.1 deraadt };
113 1.1 deraadt
114 1.68 dholland #define BUFSIZE 1400
115 1.68 dholland
116 1.95 dholland /* the list of all domains */
117 1.77 dholland static struct domain *domains;
118 1.24 christos static int check;
119 1.1 deraadt
120 1.95 dholland /* option settings */
121 1.82 dholland static ypbind_mode_t default_ypbindmode;
122 1.81 dholland static int allow_local_ypset = 0, allow_any_ypset = 0;
123 1.30 lukem static int insecure;
124 1.86 dholland
125 1.95 dholland /* the sockets we use to interact with servers */
126 1.24 christos static int rpcsock, pingsock;
127 1.95 dholland
128 1.95 dholland /* stuff used for manually interacting with servers */
129 1.24 christos static struct rmtcallargs rmtca;
130 1.24 christos static struct rmtcallres rmtcr;
131 1.26 ws static bool_t rmtcr_outval;
132 1.64 dholland static unsigned long rmtcr_port;
133 1.95 dholland
134 1.95 dholland /* The ypbind service transports */
135 1.24 christos static SVCXPRT *udptransp, *tcptransp;
136 1.24 christos
137 1.97 dholland /* set if we get SIGHUP */
138 1.97 dholland static sig_atomic_t hupped;
139 1.97 dholland
140 1.68 dholland ////////////////////////////////////////////////////////////
141 1.73 dholland // utilities
142 1.73 dholland
143 1.95 dholland /*
144 1.95 dholland * Combo of open() and flock().
145 1.95 dholland */
146 1.73 dholland static int
147 1.73 dholland open_locked(const char *path, int flags, mode_t mode)
148 1.73 dholland {
149 1.73 dholland int fd;
150 1.73 dholland
151 1.73 dholland fd = open(path, flags|O_SHLOCK, mode);
152 1.73 dholland if (fd < 0) {
153 1.73 dholland return -1;
154 1.73 dholland }
155 1.73 dholland #if O_SHLOCK == 0
156 1.73 dholland /* dholland 20110522 wouldn't it be better to check this for error? */
157 1.73 dholland (void)flock(fd, LOCK_SH);
158 1.73 dholland #endif
159 1.73 dholland return fd;
160 1.73 dholland }
161 1.73 dholland
162 1.96 dholland /*
163 1.96 dholland * Exponential backoff for pinging servers for a dead domain.
164 1.96 dholland *
165 1.96 dholland * We go 10 -> 20 -> 40 -> 60 seconds, then 2 -> 4 -> 8 -> 15 -> 30 ->
166 1.96 dholland * 60 minutes, and stay at 60 minutes. This is overengineered.
167 1.96 dholland *
168 1.96 dholland * With a 60 minute max backoff the response time for when things come
169 1.96 dholland * back is not awful, but we only try (and log) about 60 times even if
170 1.96 dholland * things are down for a whole long weekend. This is an acceptable log
171 1.96 dholland * load, I think.
172 1.96 dholland */
173 1.96 dholland static void
174 1.96 dholland backoff(unsigned *psecs)
175 1.96 dholland {
176 1.96 dholland unsigned secs;
177 1.96 dholland
178 1.96 dholland secs = *psecs;
179 1.96 dholland if (secs < 60) {
180 1.96 dholland secs *= 2;
181 1.96 dholland if (secs > 60) {
182 1.96 dholland secs = 60;
183 1.96 dholland }
184 1.96 dholland } else if (secs < 60 * 15) {
185 1.96 dholland secs *= 2;
186 1.96 dholland if (secs > 60 * 15) {
187 1.96 dholland secs = 60 * 15;
188 1.96 dholland }
189 1.96 dholland } else if (secs < 60 * 60) {
190 1.96 dholland secs *= 2;
191 1.96 dholland }
192 1.96 dholland *psecs = secs;
193 1.96 dholland }
194 1.96 dholland
195 1.73 dholland ////////////////////////////////////////////////////////////
196 1.68 dholland // logging
197 1.68 dholland
198 1.24 christos #ifdef DEBUG
199 1.68 dholland #define DPRINTF(...) (debug ? (void)printf(__VA_ARGS__) : (void)0)
200 1.68 dholland static int debug;
201 1.68 dholland #else
202 1.68 dholland #define DPRINTF(...)
203 1.24 christos #endif
204 1.36 mrg
205 1.69 dholland static void yp_log(int, const char *, ...) __printflike(2, 3);
206 1.69 dholland
207 1.95 dholland /*
208 1.95 dholland * Log some stuff, to syslog or stderr depending on the debug setting.
209 1.95 dholland */
210 1.37 bouyer static void
211 1.37 bouyer yp_log(int pri, const char *fmt, ...)
212 1.37 bouyer {
213 1.37 bouyer va_list ap;
214 1.37 bouyer
215 1.37 bouyer va_start(ap, fmt);
216 1.37 bouyer
217 1.37 bouyer #if defined(DEBUG)
218 1.72 dholland if (debug) {
219 1.57 christos (void)vprintf(fmt, ap);
220 1.72 dholland (void)printf("\n");
221 1.72 dholland } else
222 1.37 bouyer #endif
223 1.37 bouyer vsyslog(pri, fmt, ap);
224 1.37 bouyer va_end(ap);
225 1.37 bouyer }
226 1.37 bouyer
227 1.68 dholland ////////////////////////////////////////////////////////////
228 1.71 dholland // ypservers file
229 1.71 dholland
230 1.71 dholland /*
231 1.71 dholland * Get pathname for the ypservers file for a given domain
232 1.71 dholland * (/var/yp/binding/DOMAIN.ypservers)
233 1.71 dholland */
234 1.71 dholland static const char *
235 1.71 dholland ypservers_filename(const char *domain)
236 1.71 dholland {
237 1.71 dholland static char ret[PATH_MAX];
238 1.71 dholland
239 1.71 dholland (void)snprintf(ret, sizeof(ret), "%s/%s%s",
240 1.71 dholland BINDINGDIR, domain, YPSERVERSSUFF);
241 1.71 dholland return ret;
242 1.71 dholland }
243 1.71 dholland
244 1.71 dholland ////////////////////////////////////////////////////////////
245 1.75 dholland // struct domain
246 1.68 dholland
247 1.95 dholland /*
248 1.96 dholland * The state transitions of a domain work as follows:
249 1.95 dholland *
250 1.96 dholland * in state NEW:
251 1.96 dholland * nag_servers every 5 seconds
252 1.96 dholland * upon answer, state is ALIVE
253 1.96 dholland *
254 1.96 dholland * in state ALIVE:
255 1.96 dholland * every 60 seconds, send ping and switch to state PINGING
256 1.96 dholland *
257 1.96 dholland * in state PINGING:
258 1.96 dholland * upon answer, go to state ALIVE
259 1.96 dholland * if no answer in 5 seconds, go to state LOST and do nag_servers
260 1.96 dholland *
261 1.96 dholland * in state LOST:
262 1.96 dholland * do nag_servers every 5 seconds
263 1.96 dholland * upon answer, go to state ALIVE
264 1.96 dholland * if no answer in 60 seconds, go to state DEAD
265 1.96 dholland *
266 1.96 dholland * in state DEAD
267 1.96 dholland * do nag_servers every backofftime seconds (starts at 10)
268 1.96 dholland * upon answer go to state ALIVE
269 1.96 dholland * backofftime doubles (approximately) each try, with a cap of 1 hour
270 1.95 dholland */
271 1.95 dholland
272 1.95 dholland /*
273 1.95 dholland * Look up a domain by the XID we assigned it.
274 1.95 dholland */
275 1.75 dholland static struct domain *
276 1.76 dholland domain_find(uint32_t xid)
277 1.68 dholland {
278 1.78 dholland struct domain *dom;
279 1.68 dholland
280 1.78 dholland for (dom = domains; dom != NULL; dom = dom->dom_next)
281 1.78 dholland if (dom->dom_xid == xid)
282 1.68 dholland break;
283 1.78 dholland return dom;
284 1.68 dholland }
285 1.68 dholland
286 1.95 dholland /*
287 1.95 dholland * Pick an XID for a domain.
288 1.95 dholland *
289 1.95 dholland * XXX: this should just generate a random number.
290 1.95 dholland */
291 1.68 dholland static uint32_t
292 1.78 dholland unique_xid(struct domain *dom)
293 1.68 dholland {
294 1.68 dholland uint32_t tmp_xid;
295 1.68 dholland
296 1.78 dholland tmp_xid = ((uint32_t)(unsigned long)dom) & 0xffffffff;
297 1.76 dholland while (domain_find(tmp_xid) != NULL)
298 1.68 dholland tmp_xid++;
299 1.68 dholland
300 1.68 dholland return tmp_xid;
301 1.68 dholland }
302 1.68 dholland
303 1.95 dholland /*
304 1.95 dholland * Construct a new domain. Adds it to the global linked list of all
305 1.95 dholland * domains.
306 1.95 dholland */
307 1.75 dholland static struct domain *
308 1.78 dholland domain_create(const char *name)
309 1.24 christos {
310 1.78 dholland struct domain *dom;
311 1.82 dholland const char *pathname;
312 1.82 dholland struct stat st;
313 1.1 deraadt
314 1.78 dholland dom = malloc(sizeof *dom);
315 1.78 dholland if (dom == NULL) {
316 1.75 dholland yp_log(LOG_ERR, "domain_create: Out of memory");
317 1.37 bouyer exit(1);
318 1.37 bouyer }
319 1.1 deraadt
320 1.80 dholland dom->dom_next = NULL;
321 1.80 dholland
322 1.79 dholland (void)strlcpy(dom->dom_name, name, sizeof(dom->dom_name));
323 1.80 dholland (void)memset(&dom->dom_server_addr, 0, sizeof(dom->dom_server_addr));
324 1.80 dholland dom->dom_vers = YPVERS;
325 1.80 dholland dom->dom_checktime = 0;
326 1.80 dholland dom->dom_asktime = 0;
327 1.96 dholland dom->dom_losttime = 0;
328 1.96 dholland dom->dom_backofftime = 10;
329 1.80 dholland dom->dom_lockfd = -1;
330 1.96 dholland dom->dom_state = DOM_NEW;
331 1.80 dholland dom->dom_xid = unique_xid(dom);
332 1.84 dholland dom->dom_been_ypset = 0;
333 1.84 dholland dom->dom_serversfile = NULL;
334 1.80 dholland
335 1.82 dholland /*
336 1.82 dholland * Per traditional ypbind(8) semantics, if a ypservers
337 1.82 dholland * file does not exist, we revert to broadcast mode.
338 1.82 dholland *
339 1.82 dholland * The sysadmin can force broadcast mode by passing the
340 1.82 dholland * -broadcast flag. There is currently no way to fail and
341 1.82 dholland * reject domains for which there is no ypservers file.
342 1.82 dholland */
343 1.82 dholland dom->dom_ypbindmode = default_ypbindmode;
344 1.82 dholland if (dom->dom_ypbindmode == YPBIND_DIRECT) {
345 1.87 dholland pathname = ypservers_filename(dom->dom_name);
346 1.82 dholland if (stat(pathname, &st) < 0) {
347 1.82 dholland /* XXX syslog a warning here? */
348 1.82 dholland DPRINTF("%s does not exist, defaulting to broadcast\n",
349 1.82 dholland pathname);
350 1.82 dholland dom->dom_ypbindmode = YPBIND_BROADCAST;
351 1.82 dholland }
352 1.82 dholland }
353 1.82 dholland
354 1.80 dholland /* add to global list */
355 1.80 dholland dom->dom_next = domains;
356 1.80 dholland domains = dom;
357 1.80 dholland
358 1.78 dholland return dom;
359 1.24 christos }
360 1.24 christos
361 1.68 dholland ////////////////////////////////////////////////////////////
362 1.68 dholland // locks
363 1.68 dholland
364 1.95 dholland /*
365 1.95 dholland * Open a new binding file. Does not write the contents out; the
366 1.95 dholland * caller (there's only one) does that.
367 1.95 dholland */
368 1.24 christos static int
369 1.78 dholland makelock(struct domain *dom)
370 1.24 christos {
371 1.24 christos int fd;
372 1.24 christos char path[MAXPATHLEN];
373 1.20 cgd
374 1.36 mrg (void)snprintf(path, sizeof(path), "%s/%s.%ld", BINDINGDIR,
375 1.79 dholland dom->dom_name, dom->dom_vers);
376 1.24 christos
377 1.73 dholland fd = open_locked(path, O_CREAT|O_RDWR|O_TRUNC, 0644);
378 1.73 dholland if (fd == -1) {
379 1.36 mrg (void)mkdir(BINDINGDIR, 0755);
380 1.73 dholland fd = open_locked(path, O_CREAT|O_RDWR|O_TRUNC, 0644);
381 1.73 dholland if (fd == -1) {
382 1.24 christos return -1;
383 1.73 dholland }
384 1.24 christos }
385 1.24 christos
386 1.24 christos return fd;
387 1.24 christos }
388 1.24 christos
389 1.95 dholland /*
390 1.95 dholland * Remove a binding file.
391 1.95 dholland */
392 1.24 christos static void
393 1.78 dholland removelock(struct domain *dom)
394 1.24 christos {
395 1.24 christos char path[MAXPATHLEN];
396 1.24 christos
397 1.36 mrg (void)snprintf(path, sizeof(path), "%s/%s.%ld",
398 1.79 dholland BINDINGDIR, dom->dom_name, dom->dom_vers);
399 1.36 mrg (void)unlink(path);
400 1.24 christos }
401 1.24 christos
402 1.59 chuck /*
403 1.95 dholland * purge_bindingdir: remove old binding files (i.e. "rm *.[0-9]" in BINDINGDIR)
404 1.95 dholland *
405 1.95 dholland * The local YP functions [e.g. yp_master()] will fail without even
406 1.95 dholland * talking to ypbind if there is a stale (non-flock'd) binding file
407 1.95 dholland * present.
408 1.59 chuck *
409 1.95 dholland * We have to remove all binding files in BINDINGDIR, not just the one
410 1.95 dholland * for the default domain.
411 1.59 chuck */
412 1.59 chuck static int
413 1.60 christos purge_bindingdir(const char *dirpath)
414 1.61 skrll {
415 1.59 chuck DIR *dirp;
416 1.59 chuck int unlinkedfiles, l;
417 1.59 chuck struct dirent *dp;
418 1.59 chuck char pathname[MAXPATHLEN];
419 1.59 chuck
420 1.59 chuck if ((dirp = opendir(dirpath)) == NULL)
421 1.59 chuck return(-1); /* at this point, shouldn't ever happen */
422 1.59 chuck
423 1.59 chuck do {
424 1.59 chuck unlinkedfiles = 0;
425 1.59 chuck while ((dp = readdir(dirp)) != NULL) {
426 1.59 chuck l = dp->d_namlen;
427 1.59 chuck /* 'rm *.[0-9]' */
428 1.59 chuck if (l > 2 && dp->d_name[l-2] == '.' &&
429 1.59 chuck dp->d_name[l-1] >= '0' && dp->d_name[l-1] <= '9') {
430 1.59 chuck (void)snprintf(pathname, sizeof(pathname),
431 1.59 chuck "%s/%s", dirpath, dp->d_name);
432 1.59 chuck if (unlink(pathname) < 0 && errno != ENOENT)
433 1.59 chuck return(-1);
434 1.59 chuck unlinkedfiles++;
435 1.59 chuck }
436 1.59 chuck }
437 1.59 chuck
438 1.59 chuck /* rescan dir if we removed it */
439 1.59 chuck if (unlinkedfiles)
440 1.59 chuck rewinddir(dirp);
441 1.59 chuck
442 1.59 chuck } while (unlinkedfiles);
443 1.59 chuck
444 1.59 chuck closedir(dirp);
445 1.59 chuck return(0);
446 1.59 chuck }
447 1.59 chuck
448 1.68 dholland ////////////////////////////////////////////////////////////
449 1.68 dholland // sunrpc twaddle
450 1.68 dholland
451 1.68 dholland /*
452 1.92 dholland * Check if the info coming in is (at least somewhat) valid.
453 1.92 dholland */
454 1.92 dholland static int
455 1.92 dholland rpc_is_valid_response(char *name, struct sockaddr_in *addr)
456 1.92 dholland {
457 1.92 dholland if (name == NULL) {
458 1.92 dholland return 0;
459 1.92 dholland }
460 1.92 dholland
461 1.92 dholland if (_yp_invalid_domain(name)) {
462 1.92 dholland return 0;
463 1.92 dholland }
464 1.92 dholland
465 1.92 dholland /* don't support insecure servers by default */
466 1.92 dholland if (!insecure && ntohs(addr->sin_port) >= IPPORT_RESERVED) {
467 1.92 dholland return 0;
468 1.92 dholland }
469 1.92 dholland
470 1.92 dholland return 1;
471 1.92 dholland }
472 1.92 dholland
473 1.92 dholland /*
474 1.95 dholland * Take note of the fact that we've received a reply from a ypserver.
475 1.95 dholland * Or, in the case of being ypset, that we've been ypset, which
476 1.95 dholland * functions much the same.
477 1.95 dholland *
478 1.95 dholland * Note that FORCE is set if and only if IS_YPSET is set.
479 1.95 dholland *
480 1.95 dholland * This function has also for the past 20+ years carried the annotation
481 1.95 dholland *
482 1.95 dholland * LOOPBACK IS MORE IMPORTANT: PUT IN HACK
483 1.95 dholland *
484 1.95 dholland * whose meaning isn't entirely clear.
485 1.68 dholland */
486 1.69 dholland static void
487 1.83 dholland rpc_received(char *dom_name, struct sockaddr_in *raddrp, int force,
488 1.83 dholland int is_ypset)
489 1.68 dholland {
490 1.78 dholland struct domain *dom;
491 1.68 dholland struct iovec iov[2];
492 1.68 dholland struct ypbind_resp ybr;
493 1.72 dholland ssize_t result;
494 1.68 dholland int fd;
495 1.68 dholland
496 1.68 dholland DPRINTF("returned from %s about %s\n",
497 1.78 dholland inet_ntoa(raddrp->sin_addr), dom_name);
498 1.68 dholland
499 1.95 dholland /* validate some stuff */
500 1.92 dholland if (!rpc_is_valid_response(dom_name, raddrp)) {
501 1.68 dholland return;
502 1.92 dholland }
503 1.68 dholland
504 1.95 dholland /* look for the domain */
505 1.78 dholland for (dom = domains; dom != NULL; dom = dom->dom_next)
506 1.79 dholland if (!strcmp(dom->dom_name, dom_name))
507 1.68 dholland break;
508 1.68 dholland
509 1.95 dholland /* if not found, create it, but only if FORCE; otherwise ignore */
510 1.78 dholland if (dom == NULL) {
511 1.68 dholland if (force == 0)
512 1.68 dholland return;
513 1.78 dholland dom = domain_create(dom_name);
514 1.68 dholland }
515 1.68 dholland
516 1.95 dholland /* the domain needs to know if it's been explicitly ypset */
517 1.83 dholland if (is_ypset) {
518 1.83 dholland dom->dom_been_ypset = 1;
519 1.83 dholland }
520 1.83 dholland
521 1.95 dholland /*
522 1.96 dholland * If the domain is alive and we aren't being called by ypset,
523 1.96 dholland * we shouldn't be getting a response at all. Log it, as it
524 1.96 dholland * might be hostile.
525 1.96 dholland */
526 1.96 dholland if (dom->dom_state == DOM_ALIVE && force == 0) {
527 1.96 dholland if (!memcmp(&dom->dom_server_addr, raddrp,
528 1.96 dholland sizeof(dom->dom_server_addr))) {
529 1.96 dholland yp_log(LOG_WARNING,
530 1.96 dholland "Unexpected reply from server %s for domain %s",
531 1.96 dholland inet_ntoa(dom->dom_server_addr.sin_addr),
532 1.96 dholland dom->dom_name);
533 1.96 dholland } else {
534 1.96 dholland yp_log(LOG_WARNING,
535 1.96 dholland "Falsified reply from %s for domain %s",
536 1.96 dholland inet_ntoa(dom->dom_server_addr.sin_addr),
537 1.96 dholland dom->dom_name);
538 1.96 dholland }
539 1.96 dholland return;
540 1.96 dholland }
541 1.96 dholland
542 1.96 dholland /*
543 1.96 dholland * If we're expected a ping response, and we've got it
544 1.96 dholland * (meaning we aren't being called by ypset), we don't need to
545 1.96 dholland * do anything.
546 1.95 dholland */
547 1.96 dholland if (dom->dom_state == DOM_PINGING && force == 0) {
548 1.95 dholland /*
549 1.95 dholland * If the reply came from the server we expect, set
550 1.96 dholland * dom_state back to ALIVE and ping again in 60
551 1.96 dholland * seconds.
552 1.95 dholland *
553 1.96 dholland * If it came from somewhere else, log it.
554 1.95 dholland */
555 1.78 dholland if (!memcmp(&dom->dom_server_addr, raddrp,
556 1.78 dholland sizeof(dom->dom_server_addr))) {
557 1.96 dholland dom->dom_state = DOM_ALIVE;
558 1.68 dholland /* recheck binding in 60 sec */
559 1.78 dholland dom->dom_checktime = time(NULL) + 60;
560 1.96 dholland } else {
561 1.96 dholland yp_log(LOG_WARNING,
562 1.96 dholland "Falsified reply from %s for domain %s",
563 1.96 dholland inet_ntoa(dom->dom_server_addr.sin_addr),
564 1.96 dholland dom->dom_name);
565 1.68 dholland }
566 1.68 dholland return;
567 1.68 dholland }
568 1.95 dholland
569 1.96 dholland #ifdef HEURISTIC
570 1.96 dholland /*
571 1.96 dholland * If transitioning to the alive state from a non-alive state,
572 1.96 dholland * clear dom_asktime. This will help prevent any requests that
573 1.96 dholland * are still coming in from triggering unnecessary pings via
574 1.96 dholland * the HEURISTIC code.
575 1.96 dholland *
576 1.96 dholland * XXX: this may not be an adequate measure; we may need to
577 1.96 dholland * keep more state so we can disable the HEURISTIC code for
578 1.96 dholland * the first few seconds after rebinding.
579 1.96 dholland */
580 1.96 dholland if (dom->dom_state == DOM_NEW ||
581 1.96 dholland dom->dom_state == DOM_LOST ||
582 1.96 dholland dom->dom_state == DOM_DEAD) {
583 1.96 dholland dom->dom_asktime = 0;
584 1.96 dholland }
585 1.96 dholland #endif
586 1.96 dholland
587 1.95 dholland /*
588 1.95 dholland * Take the address we got the message from (or in the case of
589 1.95 dholland * ypset, the explicit address we were given) as the server
590 1.95 dholland * address for this domain, mark the domain alive, and we'll
591 1.95 dholland * check it again in 60 seconds.
592 1.95 dholland *
593 1.95 dholland * XXX: it looks like if we get a random unsolicited reply
594 1.95 dholland * from somewhere, we'll silently switch to that server
595 1.95 dholland * address, regardless of merit.
596 1.95 dholland *
597 1.95 dholland * 1. If we have a foo.ypservers file the address should be
598 1.95 dholland * checked against it and rejected if it's not one of the
599 1.95 dholland * addresses of one of the listed hostnames. Note that it
600 1.95 dholland * might not be the same address we sent to; even fairly smart
601 1.95 dholland * UDP daemons don't always handle multihomed hosts correctly
602 1.95 dholland * and we can't expect sunrpc code to do anything intelligent
603 1.95 dholland * at all.
604 1.95 dholland *
605 1.95 dholland * 2. If we're in broadcast mode the address should be
606 1.95 dholland * checked against the local addresses and netmasks so we
607 1.95 dholland * don't accept responses from Mars.
608 1.95 dholland *
609 1.95 dholland * 2a. If we're in broadcast mode and we've been ypset, we
610 1.95 dholland * should not accept anything else until we drop the ypset
611 1.95 dholland * state for not responding.
612 1.95 dholland *
613 1.95 dholland * 3. Either way we should not accept a response from an
614 1.95 dholland * arbitrary host unless we don't currently have a binding.
615 1.96 dholland * (This is now fixed above.)
616 1.95 dholland *
617 1.95 dholland * Note that for a random unsolicited reply to work it has to
618 1.95 dholland * carry the XID of one of the domains we know about; but
619 1.95 dholland * those values are predictable.
620 1.95 dholland */
621 1.78 dholland (void)memcpy(&dom->dom_server_addr, raddrp,
622 1.78 dholland sizeof(dom->dom_server_addr));
623 1.68 dholland /* recheck binding in 60 seconds */
624 1.78 dholland dom->dom_checktime = time(NULL) + 60;
625 1.96 dholland dom->dom_state = DOM_ALIVE;
626 1.96 dholland
627 1.96 dholland /* Clear the dead/backoff state. */
628 1.96 dholland dom->dom_losttime = 0;
629 1.96 dholland dom->dom_backofftime = 10;
630 1.68 dholland
631 1.95 dholland /*
632 1.95 dholland * Generate a new binding file. If this fails, forget about it.
633 1.95 dholland * (But we keep the binding and we'll report it to anyone who
634 1.95 dholland * asks via the ypbind service.) XXX: this will interact badly,
635 1.95 dholland * maybe very badly, with the code in HEURISTIC.
636 1.95 dholland *
637 1.95 dholland * Note that makelock() doesn't log on failure.
638 1.95 dholland */
639 1.95 dholland
640 1.78 dholland if (dom->dom_lockfd != -1)
641 1.78 dholland (void)close(dom->dom_lockfd);
642 1.68 dholland
643 1.78 dholland if ((fd = makelock(dom)) == -1)
644 1.68 dholland return;
645 1.68 dholland
646 1.78 dholland dom->dom_lockfd = fd;
647 1.68 dholland
648 1.68 dholland iov[0].iov_base = &(udptransp->xp_port);
649 1.68 dholland iov[0].iov_len = sizeof udptransp->xp_port;
650 1.68 dholland iov[1].iov_base = &ybr;
651 1.68 dholland iov[1].iov_len = sizeof ybr;
652 1.68 dholland
653 1.68 dholland (void)memset(&ybr, 0, sizeof ybr);
654 1.68 dholland ybr.ypbind_status = YPBIND_SUCC_VAL;
655 1.68 dholland ybr.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr =
656 1.68 dholland raddrp->sin_addr;
657 1.68 dholland ybr.ypbind_respbody.ypbind_bindinfo.ypbind_binding_port =
658 1.68 dholland raddrp->sin_port;
659 1.68 dholland
660 1.78 dholland result = writev(dom->dom_lockfd, iov, 2);
661 1.72 dholland if (result < 0 || (size_t)result != iov[0].iov_len + iov[1].iov_len) {
662 1.72 dholland if (result < 0)
663 1.72 dholland yp_log(LOG_WARNING, "writev: %s", strerror(errno));
664 1.72 dholland else
665 1.72 dholland yp_log(LOG_WARNING, "writev: short count");
666 1.78 dholland (void)close(dom->dom_lockfd);
667 1.78 dholland removelock(dom);
668 1.78 dholland dom->dom_lockfd = -1;
669 1.68 dholland }
670 1.68 dholland }
671 1.68 dholland
672 1.95 dholland /*
673 1.95 dholland * The NULL call: do nothing. This is obliged to exist because of
674 1.95 dholland * sunrpc silliness.
675 1.95 dholland */
676 1.24 christos static void *
677 1.57 christos /*ARGSUSED*/
678 1.47 wiz ypbindproc_null_2(SVCXPRT *transp, void *argp)
679 1.1 deraadt {
680 1.1 deraadt static char res;
681 1.1 deraadt
682 1.65 dholland DPRINTF("ypbindproc_null_2\n");
683 1.36 mrg (void)memset(&res, 0, sizeof(res));
684 1.1 deraadt return (void *)&res;
685 1.1 deraadt }
686 1.1 deraadt
687 1.95 dholland /*
688 1.95 dholland * The DOMAIN call: look up the ypserver for a specified domain.
689 1.95 dholland */
690 1.24 christos static void *
691 1.57 christos /*ARGSUSED*/
692 1.47 wiz ypbindproc_domain_2(SVCXPRT *transp, void *argp)
693 1.1 deraadt {
694 1.1 deraadt static struct ypbind_resp res;
695 1.78 dholland struct domain *dom;
696 1.24 christos char *arg = *(char **) argp;
697 1.9 deraadt time_t now;
698 1.30 lukem int count;
699 1.1 deraadt
700 1.65 dholland DPRINTF("ypbindproc_domain_2 %s\n", arg);
701 1.95 dholland
702 1.95 dholland /* Reject invalid domains. */
703 1.30 lukem if (_yp_invalid_domain(arg))
704 1.30 lukem return NULL;
705 1.30 lukem
706 1.36 mrg (void)memset(&res, 0, sizeof res);
707 1.1 deraadt res.ypbind_status = YPBIND_FAIL_VAL;
708 1.1 deraadt
709 1.95 dholland /*
710 1.95 dholland * Look for the domain. XXX: Behave erratically if we have
711 1.95 dholland * more than 100 domains. The intent here is to avoid allowing
712 1.95 dholland * arbitrary incoming requests to create more than 100
713 1.95 dholland * domains; but this logic means that if we legitimately have
714 1.95 dholland * more than 100 (e.g. via ypset) we'll only actually bind the
715 1.95 dholland * first 100 and the rest will fail. The test on 'count' should
716 1.95 dholland * be moved further down.
717 1.95 dholland */
718 1.78 dholland for (count = 0, dom = domains;
719 1.78 dholland dom != NULL;
720 1.78 dholland dom = dom->dom_next, count++) {
721 1.30 lukem if (count > 100)
722 1.30 lukem return NULL; /* prevent denial of service */
723 1.79 dholland if (!strcmp(dom->dom_name, arg))
724 1.1 deraadt break;
725 1.30 lukem }
726 1.1 deraadt
727 1.95 dholland /*
728 1.95 dholland * If the domain doesn't exist, create it, then fail the call
729 1.95 dholland * because we have no information yet.
730 1.95 dholland *
731 1.95 dholland * Set "check" so that checkwork() will run and look for a
732 1.95 dholland * server.
733 1.95 dholland *
734 1.95 dholland * XXX: like during startup there's a spurious call to
735 1.95 dholland * removelock() after domain_create().
736 1.95 dholland */
737 1.78 dholland if (dom == NULL) {
738 1.78 dholland dom = domain_create(arg);
739 1.78 dholland removelock(dom);
740 1.1 deraadt check++;
741 1.65 dholland DPRINTF("unknown domain %s\n", arg);
742 1.1 deraadt return NULL;
743 1.1 deraadt }
744 1.1 deraadt
745 1.96 dholland if (dom->dom_state == DOM_NEW) {
746 1.96 dholland DPRINTF("new domain %s\n", arg);
747 1.1 deraadt return NULL;
748 1.24 christos }
749 1.1 deraadt
750 1.9 deraadt #ifdef HEURISTIC
751 1.95 dholland /*
752 1.95 dholland * Keep track of the last time we were explicitly asked about
753 1.95 dholland * this domain. If it happens a lot, force a ping. This works
754 1.95 dholland * (or "works") because we only get asked specifically when
755 1.95 dholland * things aren't going; otherwise the client code in libc and
756 1.95 dholland * elsewhere uses the binding file.
757 1.95 dholland *
758 1.95 dholland * Note: HEURISTIC is enabled by default.
759 1.96 dholland *
760 1.96 dholland * dholland 20140609: I think this is part of the mechanism
761 1.96 dholland * that causes ypbind to spam. I'm changing this logic so it
762 1.96 dholland * only triggers when the state is DOM_ALIVE: if the domain
763 1.96 dholland * is new, lost, or dead we shouldn't send more requests than
764 1.96 dholland * the ones already scheduled, and if we're already in the
765 1.96 dholland * middle of pinging there's no point doing it again.
766 1.95 dholland */
767 1.57 christos (void)time(&now);
768 1.96 dholland if (dom->dom_state == DOM_ALIVE && now < dom->dom_asktime + 5) {
769 1.1 deraadt /*
770 1.9 deraadt * Hmm. More than 2 requests in 5 seconds have indicated
771 1.9 deraadt * that my binding is possibly incorrect.
772 1.9 deraadt * Ok, do an immediate poll of the server.
773 1.1 deraadt */
774 1.78 dholland if (dom->dom_checktime >= now) {
775 1.9 deraadt /* don't flood it */
776 1.78 dholland dom->dom_checktime = 0;
777 1.9 deraadt check++;
778 1.9 deraadt }
779 1.1 deraadt }
780 1.78 dholland dom->dom_asktime = now;
781 1.1 deraadt #endif
782 1.1 deraadt
783 1.1 deraadt res.ypbind_status = YPBIND_SUCC_VAL;
784 1.1 deraadt res.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr.s_addr =
785 1.78 dholland dom->dom_server_addr.sin_addr.s_addr;
786 1.1 deraadt res.ypbind_respbody.ypbind_bindinfo.ypbind_binding_port =
787 1.78 dholland dom->dom_server_addr.sin_port;
788 1.79 dholland DPRINTF("domain %s at %s/%d\n", dom->dom_name,
789 1.78 dholland inet_ntoa(dom->dom_server_addr.sin_addr),
790 1.78 dholland ntohs(dom->dom_server_addr.sin_port));
791 1.1 deraadt return &res;
792 1.1 deraadt }
793 1.1 deraadt
794 1.95 dholland /*
795 1.95 dholland * The SETDOM call: ypset.
796 1.95 dholland *
797 1.95 dholland * Unless -ypsetme was given on the command line, this is rejected;
798 1.95 dholland * even then it's only allowed from localhost unless -ypset was
799 1.95 dholland * given on the command line.
800 1.95 dholland *
801 1.95 dholland * Allowing anyone anywhere to ypset you (and therefore provide your
802 1.95 dholland * password file and such) is a horrible thing and it isn't clear to
803 1.95 dholland * me why this functionality even exists.
804 1.95 dholland *
805 1.95 dholland * ypset from localhost has some but limited utility.
806 1.95 dholland */
807 1.24 christos static void *
808 1.47 wiz ypbindproc_setdom_2(SVCXPRT *transp, void *argp)
809 1.1 deraadt {
810 1.24 christos struct ypbind_setdom *sd = argp;
811 1.1 deraadt struct sockaddr_in *fromsin, bindsin;
812 1.13 deraadt static bool_t res;
813 1.1 deraadt
814 1.36 mrg (void)memset(&res, 0, sizeof(res));
815 1.1 deraadt fromsin = svc_getcaller(transp);
816 1.88 dholland DPRINTF("ypbindproc_setdom_2 from %s\n", inet_ntoa(fromsin->sin_addr));
817 1.1 deraadt
818 1.95 dholland /*
819 1.95 dholland * Reject unless enabled.
820 1.95 dholland */
821 1.95 dholland
822 1.81 dholland if (allow_any_ypset) {
823 1.81 dholland /* nothing */
824 1.81 dholland } else if (allow_local_ypset) {
825 1.24 christos if (fromsin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
826 1.81 dholland DPRINTF("ypset denied from %s\n",
827 1.65 dholland inet_ntoa(fromsin->sin_addr));
828 1.24 christos return NULL;
829 1.24 christos }
830 1.81 dholland } else {
831 1.65 dholland DPRINTF("ypset denied\n");
832 1.24 christos return NULL;
833 1.1 deraadt }
834 1.4 deraadt
835 1.95 dholland /* Make a "security" check. */
836 1.24 christos if (ntohs(fromsin->sin_port) >= IPPORT_RESERVED) {
837 1.65 dholland DPRINTF("ypset from unprivileged port denied\n");
838 1.13 deraadt return &res;
839 1.24 christos }
840 1.1 deraadt
841 1.95 dholland /* Ignore requests we don't understand. */
842 1.24 christos if (sd->ypsetdom_vers != YPVERS) {
843 1.65 dholland DPRINTF("ypset with wrong version denied\n");
844 1.13 deraadt return &res;
845 1.24 christos }
846 1.1 deraadt
847 1.95 dholland /*
848 1.95 dholland * Fetch the arguments out of the xdr-decoded blob and call
849 1.95 dholland * rpc_received(), setting FORCE so that the domain will be
850 1.95 dholland * created if we don't already know about it, and also saying
851 1.95 dholland * that it's actually a ypset.
852 1.95 dholland *
853 1.95 dholland * Effectively we're telilng rpc_received() that we got an
854 1.95 dholland * RPC response from the server specified by ypset.
855 1.95 dholland */
856 1.36 mrg (void)memset(&bindsin, 0, sizeof bindsin);
857 1.1 deraadt bindsin.sin_family = AF_INET;
858 1.17 mycroft bindsin.sin_len = sizeof(bindsin);
859 1.24 christos bindsin.sin_addr = sd->ypsetdom_addr;
860 1.24 christos bindsin.sin_port = sd->ypsetdom_port;
861 1.83 dholland rpc_received(sd->ypsetdom_domain, &bindsin, 1, 1);
862 1.24 christos
863 1.85 dholland DPRINTF("ypset to %s for domain %s succeeded\n",
864 1.85 dholland inet_ntoa(bindsin.sin_addr), sd->ypsetdom_domain);
865 1.1 deraadt res = 1;
866 1.13 deraadt return &res;
867 1.1 deraadt }
868 1.1 deraadt
869 1.95 dholland /*
870 1.95 dholland * Dispatcher for the ypbind service.
871 1.95 dholland *
872 1.95 dholland * There are three calls: NULL, which does nothing, DOMAIN, which
873 1.95 dholland * gets the binding for a particular domain, and SETDOM, which
874 1.95 dholland * does ypset.
875 1.95 dholland */
876 1.1 deraadt static void
877 1.47 wiz ypbindprog_2(struct svc_req *rqstp, register SVCXPRT *transp)
878 1.1 deraadt {
879 1.1 deraadt union {
880 1.22 thorpej char ypbindproc_domain_2_arg[YPMAXDOMAIN + 1];
881 1.1 deraadt struct ypbind_setdom ypbindproc_setdom_2_arg;
882 1.56 tron void *alignment;
883 1.1 deraadt } argument;
884 1.1 deraadt struct authunix_parms *creds;
885 1.1 deraadt char *result;
886 1.24 christos xdrproc_t xdr_argument, xdr_result;
887 1.47 wiz void *(*local)(SVCXPRT *, void *);
888 1.1 deraadt
889 1.1 deraadt switch (rqstp->rq_proc) {
890 1.1 deraadt case YPBINDPROC_NULL:
891 1.90 plunky xdr_argument = (xdrproc_t)xdr_void;
892 1.90 plunky xdr_result = (xdrproc_t)xdr_void;
893 1.24 christos local = ypbindproc_null_2;
894 1.1 deraadt break;
895 1.1 deraadt
896 1.1 deraadt case YPBINDPROC_DOMAIN:
897 1.90 plunky xdr_argument = (xdrproc_t)xdr_ypdomain_wrap_string;
898 1.90 plunky xdr_result = (xdrproc_t)xdr_ypbind_resp;
899 1.24 christos local = ypbindproc_domain_2;
900 1.1 deraadt break;
901 1.1 deraadt
902 1.1 deraadt case YPBINDPROC_SETDOM:
903 1.17 mycroft switch (rqstp->rq_cred.oa_flavor) {
904 1.1 deraadt case AUTH_UNIX:
905 1.1 deraadt creds = (struct authunix_parms *)rqstp->rq_clntcred;
906 1.17 mycroft if (creds->aup_uid != 0) {
907 1.1 deraadt svcerr_auth(transp, AUTH_BADCRED);
908 1.1 deraadt return;
909 1.1 deraadt }
910 1.1 deraadt break;
911 1.1 deraadt default:
912 1.1 deraadt svcerr_auth(transp, AUTH_TOOWEAK);
913 1.1 deraadt return;
914 1.1 deraadt }
915 1.1 deraadt
916 1.90 plunky xdr_argument = (xdrproc_t)xdr_ypbind_setdom;
917 1.90 plunky xdr_result = (xdrproc_t)xdr_void;
918 1.24 christos local = ypbindproc_setdom_2;
919 1.1 deraadt break;
920 1.1 deraadt
921 1.1 deraadt default:
922 1.1 deraadt svcerr_noproc(transp);
923 1.1 deraadt return;
924 1.1 deraadt }
925 1.36 mrg (void)memset(&argument, 0, sizeof(argument));
926 1.57 christos if (!svc_getargs(transp, xdr_argument, (caddr_t)(void *)&argument)) {
927 1.1 deraadt svcerr_decode(transp);
928 1.1 deraadt return;
929 1.1 deraadt }
930 1.24 christos result = (*local)(transp, &argument);
931 1.1 deraadt if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
932 1.1 deraadt svcerr_systemerr(transp);
933 1.1 deraadt }
934 1.1 deraadt return;
935 1.1 deraadt }
936 1.1 deraadt
937 1.95 dholland /*
938 1.95 dholland * Set up sunrpc stuff.
939 1.95 dholland *
940 1.95 dholland * This sets up the ypbind service (both TCP and UDP) and also opens
941 1.95 dholland * the sockets we use for talking to ypservers.
942 1.95 dholland */
943 1.74 dholland static void
944 1.74 dholland sunrpc_setup(void)
945 1.74 dholland {
946 1.74 dholland int one;
947 1.74 dholland
948 1.74 dholland (void)pmap_unset(YPBINDPROG, YPBINDVERS);
949 1.74 dholland
950 1.74 dholland udptransp = svcudp_create(RPC_ANYSOCK);
951 1.74 dholland if (udptransp == NULL)
952 1.74 dholland errx(1, "Cannot create udp service.");
953 1.74 dholland
954 1.74 dholland if (!svc_register(udptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
955 1.74 dholland IPPROTO_UDP))
956 1.74 dholland errx(1, "Unable to register (YPBINDPROG, YPBINDVERS, udp).");
957 1.74 dholland
958 1.74 dholland tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0);
959 1.74 dholland if (tcptransp == NULL)
960 1.74 dholland errx(1, "Cannot create tcp service.");
961 1.74 dholland
962 1.74 dholland if (!svc_register(tcptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
963 1.74 dholland IPPROTO_TCP))
964 1.74 dholland errx(1, "Unable to register (YPBINDPROG, YPBINDVERS, tcp).");
965 1.74 dholland
966 1.74 dholland /* XXX use SOCK_STREAM for direct queries? */
967 1.74 dholland if ((rpcsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
968 1.74 dholland err(1, "rpc socket");
969 1.74 dholland if ((pingsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
970 1.74 dholland err(1, "ping socket");
971 1.74 dholland
972 1.74 dholland (void)fcntl(rpcsock, F_SETFL, fcntl(rpcsock, F_GETFL, 0) | FNDELAY);
973 1.74 dholland (void)fcntl(pingsock, F_SETFL, fcntl(pingsock, F_GETFL, 0) | FNDELAY);
974 1.74 dholland
975 1.74 dholland one = 1;
976 1.74 dholland (void)setsockopt(rpcsock, SOL_SOCKET, SO_BROADCAST, &one,
977 1.74 dholland (socklen_t)sizeof(one));
978 1.74 dholland rmtca.prog = YPPROG;
979 1.74 dholland rmtca.vers = YPVERS;
980 1.74 dholland rmtca.proc = YPPROC_DOMAIN_NONACK;
981 1.74 dholland rmtca.xdr_args = NULL; /* set at call time */
982 1.74 dholland rmtca.args_ptr = NULL; /* set at call time */
983 1.74 dholland rmtcr.port_ptr = &rmtcr_port;
984 1.90 plunky rmtcr.xdr_results = (xdrproc_t)xdr_bool;
985 1.74 dholland rmtcr.results_ptr = (caddr_t)(void *)&rmtcr_outval;
986 1.74 dholland }
987 1.74 dholland
988 1.68 dholland ////////////////////////////////////////////////////////////
989 1.68 dholland // operational logic
990 1.68 dholland
991 1.95 dholland /*
992 1.95 dholland * Broadcast an RPC packet to hopefully contact some servers for a
993 1.95 dholland * domain.
994 1.95 dholland */
995 1.68 dholland static int
996 1.68 dholland broadcast(char *buf, int outlen)
997 1.1 deraadt {
998 1.68 dholland struct ifaddrs *ifap, *ifa;
999 1.68 dholland struct sockaddr_in bindsin;
1000 1.68 dholland struct in_addr in;
1001 1.68 dholland
1002 1.68 dholland (void)memset(&bindsin, 0, sizeof bindsin);
1003 1.68 dholland bindsin.sin_family = AF_INET;
1004 1.68 dholland bindsin.sin_len = sizeof(bindsin);
1005 1.68 dholland bindsin.sin_port = htons(PMAPPORT);
1006 1.1 deraadt
1007 1.68 dholland if (getifaddrs(&ifap) != 0) {
1008 1.72 dholland yp_log(LOG_WARNING, "broadcast: getifaddrs: %s",
1009 1.72 dholland strerror(errno));
1010 1.68 dholland return (-1);
1011 1.68 dholland }
1012 1.68 dholland for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
1013 1.68 dholland if (ifa->ifa_addr->sa_family != AF_INET)
1014 1.68 dholland continue;
1015 1.68 dholland if ((ifa->ifa_flags & IFF_UP) == 0)
1016 1.68 dholland continue;
1017 1.1 deraadt
1018 1.68 dholland switch (ifa->ifa_flags & (IFF_LOOPBACK | IFF_BROADCAST)) {
1019 1.68 dholland case IFF_BROADCAST:
1020 1.68 dholland if (!ifa->ifa_broadaddr)
1021 1.68 dholland continue;
1022 1.68 dholland if (ifa->ifa_broadaddr->sa_family != AF_INET)
1023 1.68 dholland continue;
1024 1.68 dholland in = ((struct sockaddr_in *)(void *)ifa->ifa_broadaddr)->sin_addr;
1025 1.68 dholland break;
1026 1.68 dholland case IFF_LOOPBACK:
1027 1.68 dholland in = ((struct sockaddr_in *)(void *)ifa->ifa_addr)->sin_addr;
1028 1.68 dholland break;
1029 1.68 dholland default:
1030 1.68 dholland continue;
1031 1.68 dholland }
1032 1.27 thorpej
1033 1.68 dholland bindsin.sin_addr = in;
1034 1.68 dholland DPRINTF("broadcast %x\n", bindsin.sin_addr.s_addr);
1035 1.68 dholland if (sendto(rpcsock, buf, outlen, 0,
1036 1.68 dholland (struct sockaddr *)(void *)&bindsin,
1037 1.68 dholland (socklen_t)bindsin.sin_len) == -1)
1038 1.72 dholland yp_log(LOG_WARNING, "broadcast: sendto: %s",
1039 1.72 dholland strerror(errno));
1040 1.1 deraadt }
1041 1.68 dholland freeifaddrs(ifap);
1042 1.68 dholland return (0);
1043 1.68 dholland }
1044 1.1 deraadt
1045 1.95 dholland /*
1046 1.95 dholland * Send an RPC packet to all the configured (in /var/yp/foo.ypservers)
1047 1.95 dholland * servers for a domain.
1048 1.95 dholland *
1049 1.95 dholland * XXX: we should read and parse the file up front and reread it only
1050 1.95 dholland * if it changes.
1051 1.95 dholland */
1052 1.68 dholland static int
1053 1.84 dholland direct(char *buf, int outlen, struct domain *dom)
1054 1.68 dholland {
1055 1.71 dholland const char *path;
1056 1.68 dholland char line[_POSIX2_LINE_MAX];
1057 1.68 dholland char *p;
1058 1.68 dholland struct hostent *hp;
1059 1.68 dholland struct sockaddr_in bindsin;
1060 1.68 dholland int i, count = 0;
1061 1.37 bouyer
1062 1.84 dholland /*
1063 1.84 dholland * XXX what happens if someone's editor unlinks and replaces
1064 1.84 dholland * the servers file?
1065 1.84 dholland */
1066 1.84 dholland
1067 1.84 dholland if (dom->dom_serversfile != NULL) {
1068 1.84 dholland rewind(dom->dom_serversfile);
1069 1.84 dholland } else {
1070 1.84 dholland path = ypservers_filename(dom->dom_name);
1071 1.84 dholland dom->dom_serversfile = fopen(path, "r");
1072 1.84 dholland if (dom->dom_serversfile == NULL) {
1073 1.84 dholland /*
1074 1.84 dholland * XXX there should be a time restriction on
1075 1.84 dholland * this (and/or on trying the open) so we
1076 1.84 dholland * don't flood the log. Or should we fall back
1077 1.84 dholland * to broadcast mode?
1078 1.84 dholland */
1079 1.84 dholland yp_log(LOG_ERR, "%s: %s", path,
1080 1.72 dholland strerror(errno));
1081 1.84 dholland return -1;
1082 1.37 bouyer }
1083 1.27 thorpej }
1084 1.27 thorpej
1085 1.57 christos (void)memset(&bindsin, 0, sizeof bindsin);
1086 1.27 thorpej bindsin.sin_family = AF_INET;
1087 1.27 thorpej bindsin.sin_len = sizeof(bindsin);
1088 1.27 thorpej bindsin.sin_port = htons(PMAPPORT);
1089 1.27 thorpej
1090 1.84 dholland while (fgets(line, (int)sizeof(line), dom->dom_serversfile) != NULL) {
1091 1.27 thorpej /* skip lines that are too big */
1092 1.27 thorpej p = strchr(line, '\n');
1093 1.27 thorpej if (p == NULL) {
1094 1.27 thorpej int c;
1095 1.27 thorpej
1096 1.84 dholland while ((c = getc(dom->dom_serversfile)) != '\n' && c != EOF)
1097 1.27 thorpej ;
1098 1.27 thorpej continue;
1099 1.27 thorpej }
1100 1.27 thorpej *p = '\0';
1101 1.27 thorpej p = line;
1102 1.53 dsl while (isspace((unsigned char)*p))
1103 1.27 thorpej p++;
1104 1.27 thorpej if (*p == '#')
1105 1.27 thorpej continue;
1106 1.27 thorpej hp = gethostbyname(p);
1107 1.27 thorpej if (!hp) {
1108 1.45 lukem yp_log(LOG_WARNING, "%s: %s", p, hstrerror(h_errno));
1109 1.27 thorpej continue;
1110 1.27 thorpej }
1111 1.27 thorpej /* step through all addresses in case first is unavailable */
1112 1.27 thorpej for (i = 0; hp->h_addr_list[i]; i++) {
1113 1.57 christos (void)memcpy(&bindsin.sin_addr, hp->h_addr_list[0],
1114 1.27 thorpej hp->h_length);
1115 1.27 thorpej if (sendto(rpcsock, buf, outlen, 0,
1116 1.57 christos (struct sockaddr *)(void *)&bindsin,
1117 1.57 christos (socklen_t)sizeof(bindsin)) < 0) {
1118 1.72 dholland yp_log(LOG_WARNING, "direct: sendto: %s",
1119 1.72 dholland strerror(errno));
1120 1.27 thorpej continue;
1121 1.27 thorpej } else
1122 1.27 thorpej count++;
1123 1.27 thorpej }
1124 1.27 thorpej }
1125 1.37 bouyer if (!count) {
1126 1.72 dholland yp_log(LOG_WARNING, "No contactable servers found in %s",
1127 1.84 dholland ypservers_filename(dom->dom_name));
1128 1.37 bouyer return -1;
1129 1.37 bouyer }
1130 1.27 thorpej return 0;
1131 1.27 thorpej }
1132 1.27 thorpej
1133 1.95 dholland /*
1134 1.95 dholland * Send an RPC packet to the server that's been selected with ypset.
1135 1.95 dholland * (This is only used when in broadcast mode and when ypset is
1136 1.95 dholland * allowed.)
1137 1.95 dholland */
1138 1.27 thorpej static int
1139 1.78 dholland direct_set(char *buf, int outlen, struct domain *dom)
1140 1.27 thorpej {
1141 1.27 thorpej struct sockaddr_in bindsin;
1142 1.27 thorpej char path[MAXPATHLEN];
1143 1.27 thorpej struct iovec iov[2];
1144 1.27 thorpej struct ypbind_resp ybr;
1145 1.27 thorpej SVCXPRT dummy_svc;
1146 1.57 christos int fd;
1147 1.57 christos ssize_t bytes;
1148 1.27 thorpej
1149 1.27 thorpej /*
1150 1.27 thorpej * Gack, we lose if binding file went away. We reset
1151 1.27 thorpej * "been_set" if this happens, otherwise we'll never
1152 1.27 thorpej * bind again.
1153 1.27 thorpej */
1154 1.57 christos (void)snprintf(path, sizeof(path), "%s/%s.%ld", BINDINGDIR,
1155 1.79 dholland dom->dom_name, dom->dom_vers);
1156 1.27 thorpej
1157 1.73 dholland fd = open_locked(path, O_RDONLY, 0644);
1158 1.73 dholland if (fd == -1) {
1159 1.72 dholland yp_log(LOG_WARNING, "%s: %s", path, strerror(errno));
1160 1.83 dholland dom->dom_been_ypset = 0;
1161 1.27 thorpej return -1;
1162 1.27 thorpej }
1163 1.27 thorpej
1164 1.27 thorpej /* Read the binding file... */
1165 1.57 christos iov[0].iov_base = &(dummy_svc.xp_port);
1166 1.27 thorpej iov[0].iov_len = sizeof(dummy_svc.xp_port);
1167 1.57 christos iov[1].iov_base = &ybr;
1168 1.27 thorpej iov[1].iov_len = sizeof(ybr);
1169 1.27 thorpej bytes = readv(fd, iov, 2);
1170 1.27 thorpej (void)close(fd);
1171 1.72 dholland if (bytes <0 || (size_t)bytes != (iov[0].iov_len + iov[1].iov_len)) {
1172 1.27 thorpej /* Binding file corrupt? */
1173 1.72 dholland if (bytes < 0)
1174 1.72 dholland yp_log(LOG_WARNING, "%s: %s", path, strerror(errno));
1175 1.72 dholland else
1176 1.72 dholland yp_log(LOG_WARNING, "%s: short read", path);
1177 1.83 dholland dom->dom_been_ypset = 0;
1178 1.27 thorpej return -1;
1179 1.68 dholland }
1180 1.68 dholland
1181 1.68 dholland bindsin.sin_addr =
1182 1.68 dholland ybr.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr;
1183 1.68 dholland
1184 1.68 dholland if (sendto(rpcsock, buf, outlen, 0,
1185 1.68 dholland (struct sockaddr *)(void *)&bindsin,
1186 1.68 dholland (socklen_t)sizeof(bindsin)) < 0) {
1187 1.72 dholland yp_log(LOG_WARNING, "direct_set: sendto: %s", strerror(errno));
1188 1.68 dholland return -1;
1189 1.68 dholland }
1190 1.68 dholland
1191 1.68 dholland return 0;
1192 1.68 dholland }
1193 1.68 dholland
1194 1.95 dholland /*
1195 1.95 dholland * Receive and dispatch packets on the general RPC socket.
1196 1.95 dholland */
1197 1.68 dholland static enum clnt_stat
1198 1.68 dholland handle_replies(void)
1199 1.68 dholland {
1200 1.68 dholland char buf[BUFSIZE];
1201 1.68 dholland socklen_t fromlen;
1202 1.68 dholland ssize_t inlen;
1203 1.78 dholland struct domain *dom;
1204 1.68 dholland struct sockaddr_in raddr;
1205 1.68 dholland struct rpc_msg msg;
1206 1.68 dholland XDR xdr;
1207 1.68 dholland
1208 1.68 dholland recv_again:
1209 1.68 dholland DPRINTF("handle_replies receiving\n");
1210 1.68 dholland (void)memset(&xdr, 0, sizeof(xdr));
1211 1.68 dholland (void)memset(&msg, 0, sizeof(msg));
1212 1.68 dholland msg.acpted_rply.ar_verf = _null_auth;
1213 1.68 dholland msg.acpted_rply.ar_results.where = (caddr_t)(void *)&rmtcr;
1214 1.90 plunky msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_rmtcallres;
1215 1.68 dholland
1216 1.68 dholland try_again:
1217 1.68 dholland fromlen = sizeof(struct sockaddr);
1218 1.68 dholland inlen = recvfrom(rpcsock, buf, sizeof buf, 0,
1219 1.68 dholland (struct sockaddr *)(void *)&raddr, &fromlen);
1220 1.68 dholland if (inlen < 0) {
1221 1.68 dholland if (errno == EINTR)
1222 1.68 dholland goto try_again;
1223 1.68 dholland DPRINTF("handle_replies: recvfrom failed (%s)\n",
1224 1.68 dholland strerror(errno));
1225 1.68 dholland return RPC_CANTRECV;
1226 1.68 dholland }
1227 1.68 dholland if ((size_t)inlen < sizeof(uint32_t))
1228 1.68 dholland goto recv_again;
1229 1.27 thorpej
1230 1.68 dholland /*
1231 1.68 dholland * see if reply transaction id matches sent id.
1232 1.68 dholland * If so, decode the results.
1233 1.68 dholland */
1234 1.68 dholland xdrmem_create(&xdr, buf, (unsigned)inlen, XDR_DECODE);
1235 1.68 dholland if (xdr_replymsg(&xdr, &msg)) {
1236 1.68 dholland if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
1237 1.68 dholland (msg.acpted_rply.ar_stat == SUCCESS)) {
1238 1.68 dholland raddr.sin_port = htons((uint16_t)rmtcr_port);
1239 1.78 dholland dom = domain_find(msg.rm_xid);
1240 1.78 dholland if (dom != NULL)
1241 1.83 dholland rpc_received(dom->dom_name, &raddr, 0, 0);
1242 1.68 dholland }
1243 1.27 thorpej }
1244 1.68 dholland xdr.x_op = XDR_FREE;
1245 1.90 plunky msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
1246 1.68 dholland xdr_destroy(&xdr);
1247 1.27 thorpej
1248 1.68 dholland return RPC_SUCCESS;
1249 1.1 deraadt }
1250 1.1 deraadt
1251 1.95 dholland /*
1252 1.95 dholland * Receive and dispatch packets on the ping socket.
1253 1.95 dholland */
1254 1.24 christos static enum clnt_stat
1255 1.68 dholland handle_ping(void)
1256 1.1 deraadt {
1257 1.24 christos char buf[BUFSIZE];
1258 1.54 mrg socklen_t fromlen;
1259 1.57 christos ssize_t inlen;
1260 1.78 dholland struct domain *dom;
1261 1.1 deraadt struct sockaddr_in raddr;
1262 1.1 deraadt struct rpc_msg msg;
1263 1.1 deraadt XDR xdr;
1264 1.68 dholland bool_t res;
1265 1.1 deraadt
1266 1.1 deraadt recv_again:
1267 1.68 dholland DPRINTF("handle_ping receiving\n");
1268 1.36 mrg (void)memset(&xdr, 0, sizeof(xdr));
1269 1.36 mrg (void)memset(&msg, 0, sizeof(msg));
1270 1.1 deraadt msg.acpted_rply.ar_verf = _null_auth;
1271 1.68 dholland msg.acpted_rply.ar_results.where = (caddr_t)(void *)&res;
1272 1.90 plunky msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_bool;
1273 1.1 deraadt
1274 1.1 deraadt try_again:
1275 1.68 dholland fromlen = sizeof (struct sockaddr);
1276 1.68 dholland inlen = recvfrom(pingsock, buf, sizeof buf, 0,
1277 1.68 dholland (struct sockaddr *)(void *)&raddr, &fromlen);
1278 1.17 mycroft if (inlen < 0) {
1279 1.17 mycroft if (errno == EINTR)
1280 1.1 deraadt goto try_again;
1281 1.68 dholland DPRINTF("handle_ping: recvfrom failed (%s)\n",
1282 1.65 dholland strerror(errno));
1283 1.1 deraadt return RPC_CANTRECV;
1284 1.1 deraadt }
1285 1.64 dholland if ((size_t)inlen < sizeof(uint32_t))
1286 1.1 deraadt goto recv_again;
1287 1.1 deraadt
1288 1.1 deraadt /*
1289 1.1 deraadt * see if reply transaction id matches sent id.
1290 1.1 deraadt * If so, decode the results.
1291 1.1 deraadt */
1292 1.64 dholland xdrmem_create(&xdr, buf, (unsigned)inlen, XDR_DECODE);
1293 1.17 mycroft if (xdr_replymsg(&xdr, &msg)) {
1294 1.17 mycroft if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
1295 1.17 mycroft (msg.acpted_rply.ar_stat == SUCCESS)) {
1296 1.78 dholland dom = domain_find(msg.rm_xid);
1297 1.78 dholland if (dom != NULL)
1298 1.83 dholland rpc_received(dom->dom_name, &raddr, 0, 0);
1299 1.1 deraadt }
1300 1.1 deraadt }
1301 1.1 deraadt xdr.x_op = XDR_FREE;
1302 1.90 plunky msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
1303 1.1 deraadt xdr_destroy(&xdr);
1304 1.1 deraadt
1305 1.1 deraadt return RPC_SUCCESS;
1306 1.1 deraadt }
1307 1.1 deraadt
1308 1.95 dholland /*
1309 1.95 dholland * Contact all known servers for a domain in the hopes that one of
1310 1.95 dholland * them's awake. Also, if we previously had a binding but it timed
1311 1.95 dholland * out, try the portmapper on that host in case ypserv moved ports for
1312 1.95 dholland * some reason.
1313 1.95 dholland *
1314 1.95 dholland * As a side effect, wipe out any existing binding file.
1315 1.95 dholland */
1316 1.68 dholland static int
1317 1.78 dholland nag_servers(struct domain *dom)
1318 1.68 dholland {
1319 1.79 dholland char *dom_name = dom->dom_name;
1320 1.68 dholland struct rpc_msg msg;
1321 1.68 dholland char buf[BUFSIZE];
1322 1.68 dholland enum clnt_stat st;
1323 1.68 dholland int outlen;
1324 1.68 dholland AUTH *rpcua;
1325 1.68 dholland XDR xdr;
1326 1.68 dholland
1327 1.68 dholland DPRINTF("nag_servers\n");
1328 1.90 plunky rmtca.xdr_args = (xdrproc_t)xdr_ypdomain_wrap_string;
1329 1.78 dholland rmtca.args_ptr = (caddr_t)(void *)&dom_name;
1330 1.68 dholland
1331 1.68 dholland (void)memset(&xdr, 0, sizeof xdr);
1332 1.68 dholland (void)memset(&msg, 0, sizeof msg);
1333 1.68 dholland
1334 1.68 dholland rpcua = authunix_create_default();
1335 1.68 dholland if (rpcua == NULL) {
1336 1.68 dholland DPRINTF("cannot get unix auth\n");
1337 1.68 dholland return RPC_SYSTEMERROR;
1338 1.68 dholland }
1339 1.68 dholland msg.rm_direction = CALL;
1340 1.68 dholland msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
1341 1.68 dholland msg.rm_call.cb_prog = PMAPPROG;
1342 1.68 dholland msg.rm_call.cb_vers = PMAPVERS;
1343 1.68 dholland msg.rm_call.cb_proc = PMAPPROC_CALLIT;
1344 1.68 dholland msg.rm_call.cb_cred = rpcua->ah_cred;
1345 1.68 dholland msg.rm_call.cb_verf = rpcua->ah_verf;
1346 1.68 dholland
1347 1.78 dholland msg.rm_xid = dom->dom_xid;
1348 1.68 dholland xdrmem_create(&xdr, buf, (unsigned)sizeof(buf), XDR_ENCODE);
1349 1.68 dholland if (!xdr_callmsg(&xdr, &msg)) {
1350 1.68 dholland st = RPC_CANTENCODEARGS;
1351 1.68 dholland AUTH_DESTROY(rpcua);
1352 1.68 dholland return st;
1353 1.68 dholland }
1354 1.68 dholland if (!xdr_rmtcall_args(&xdr, &rmtca)) {
1355 1.68 dholland st = RPC_CANTENCODEARGS;
1356 1.68 dholland AUTH_DESTROY(rpcua);
1357 1.68 dholland return st;
1358 1.68 dholland }
1359 1.68 dholland outlen = (int)xdr_getpos(&xdr);
1360 1.68 dholland xdr_destroy(&xdr);
1361 1.68 dholland if (outlen < 1) {
1362 1.68 dholland st = RPC_CANTENCODEARGS;
1363 1.68 dholland AUTH_DESTROY(rpcua);
1364 1.68 dholland return st;
1365 1.68 dholland }
1366 1.68 dholland AUTH_DESTROY(rpcua);
1367 1.68 dholland
1368 1.78 dholland if (dom->dom_lockfd != -1) {
1369 1.78 dholland (void)close(dom->dom_lockfd);
1370 1.78 dholland dom->dom_lockfd = -1;
1371 1.78 dholland removelock(dom);
1372 1.68 dholland }
1373 1.68 dholland
1374 1.96 dholland if (dom->dom_state == DOM_PINGING || dom->dom_state == DOM_LOST) {
1375 1.68 dholland /*
1376 1.68 dholland * This resolves the following situation:
1377 1.68 dholland * ypserver on other subnet was once bound,
1378 1.68 dholland * but rebooted and is now using a different port
1379 1.68 dholland */
1380 1.68 dholland struct sockaddr_in bindsin;
1381 1.68 dholland
1382 1.68 dholland (void)memset(&bindsin, 0, sizeof bindsin);
1383 1.68 dholland bindsin.sin_family = AF_INET;
1384 1.68 dholland bindsin.sin_len = sizeof(bindsin);
1385 1.68 dholland bindsin.sin_port = htons(PMAPPORT);
1386 1.78 dholland bindsin.sin_addr = dom->dom_server_addr.sin_addr;
1387 1.68 dholland
1388 1.68 dholland if (sendto(rpcsock, buf, outlen, 0,
1389 1.68 dholland (struct sockaddr *)(void *)&bindsin,
1390 1.68 dholland (socklen_t)sizeof bindsin) == -1)
1391 1.72 dholland yp_log(LOG_WARNING, "nag_servers: sendto: %s",
1392 1.72 dholland strerror(errno));
1393 1.68 dholland }
1394 1.68 dholland
1395 1.82 dholland switch (dom->dom_ypbindmode) {
1396 1.81 dholland case YPBIND_BROADCAST:
1397 1.83 dholland if (dom->dom_been_ypset) {
1398 1.78 dholland return direct_set(buf, outlen, dom);
1399 1.81 dholland }
1400 1.68 dholland return broadcast(buf, outlen);
1401 1.68 dholland
1402 1.68 dholland case YPBIND_DIRECT:
1403 1.84 dholland return direct(buf, outlen, dom);
1404 1.68 dholland }
1405 1.68 dholland /*NOTREACHED*/
1406 1.68 dholland return -1;
1407 1.68 dholland }
1408 1.68 dholland
1409 1.95 dholland /*
1410 1.95 dholland * Send a ping message to a domain's current ypserver.
1411 1.95 dholland */
1412 1.69 dholland static int
1413 1.78 dholland ping(struct domain *dom)
1414 1.10 deraadt {
1415 1.79 dholland char *dom_name = dom->dom_name;
1416 1.68 dholland struct rpc_msg msg;
1417 1.24 christos char buf[BUFSIZE];
1418 1.68 dholland enum clnt_stat st;
1419 1.68 dholland int outlen;
1420 1.68 dholland AUTH *rpcua;
1421 1.68 dholland XDR xdr;
1422 1.68 dholland
1423 1.68 dholland (void)memset(&xdr, 0, sizeof xdr);
1424 1.68 dholland (void)memset(&msg, 0, sizeof msg);
1425 1.68 dholland
1426 1.68 dholland rpcua = authunix_create_default();
1427 1.68 dholland if (rpcua == NULL) {
1428 1.68 dholland DPRINTF("cannot get unix auth\n");
1429 1.68 dholland return RPC_SYSTEMERROR;
1430 1.68 dholland }
1431 1.68 dholland
1432 1.68 dholland msg.rm_direction = CALL;
1433 1.68 dholland msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
1434 1.68 dholland msg.rm_call.cb_prog = YPPROG;
1435 1.68 dholland msg.rm_call.cb_vers = YPVERS;
1436 1.68 dholland msg.rm_call.cb_proc = YPPROC_DOMAIN_NONACK;
1437 1.68 dholland msg.rm_call.cb_cred = rpcua->ah_cred;
1438 1.68 dholland msg.rm_call.cb_verf = rpcua->ah_verf;
1439 1.68 dholland
1440 1.78 dholland msg.rm_xid = dom->dom_xid;
1441 1.68 dholland xdrmem_create(&xdr, buf, (unsigned)sizeof(buf), XDR_ENCODE);
1442 1.68 dholland if (!xdr_callmsg(&xdr, &msg)) {
1443 1.68 dholland st = RPC_CANTENCODEARGS;
1444 1.68 dholland AUTH_DESTROY(rpcua);
1445 1.68 dholland return st;
1446 1.68 dholland }
1447 1.78 dholland if (!xdr_ypdomain_wrap_string(&xdr, &dom_name)) {
1448 1.68 dholland st = RPC_CANTENCODEARGS;
1449 1.68 dholland AUTH_DESTROY(rpcua);
1450 1.68 dholland return st;
1451 1.68 dholland }
1452 1.68 dholland outlen = (int)xdr_getpos(&xdr);
1453 1.68 dholland xdr_destroy(&xdr);
1454 1.68 dholland if (outlen < 1) {
1455 1.68 dholland st = RPC_CANTENCODEARGS;
1456 1.68 dholland AUTH_DESTROY(rpcua);
1457 1.68 dholland return st;
1458 1.68 dholland }
1459 1.68 dholland AUTH_DESTROY(rpcua);
1460 1.68 dholland
1461 1.78 dholland DPRINTF("ping %x\n", dom->dom_server_addr.sin_addr.s_addr);
1462 1.68 dholland
1463 1.68 dholland if (sendto(pingsock, buf, outlen, 0,
1464 1.78 dholland (struct sockaddr *)(void *)&dom->dom_server_addr,
1465 1.78 dholland (socklen_t)(sizeof dom->dom_server_addr)) == -1)
1466 1.72 dholland yp_log(LOG_WARNING, "ping: sendto: %s", strerror(errno));
1467 1.68 dholland return 0;
1468 1.68 dholland
1469 1.68 dholland }
1470 1.68 dholland
1471 1.68 dholland /*
1472 1.95 dholland * Scan for timer-based work to do.
1473 1.95 dholland *
1474 1.95 dholland * If the domain is currently alive, ping the server we're currently
1475 1.95 dholland * bound to. Otherwise, try all known servers and/or broadcast for a
1476 1.95 dholland * server via nag_servers.
1477 1.68 dholland *
1478 1.95 dholland * Try again in five seconds.
1479 1.96 dholland *
1480 1.96 dholland * If we get back here and the state is still DOM_PINGING, it means
1481 1.96 dholland * we didn't receive a ping response within five seconds. Declare the
1482 1.96 dholland * binding lost. If the binding is already lost, and it's been lost
1483 1.96 dholland * for 60 seconds, switch to DOM_DEAD and begin exponential backoff.
1484 1.96 dholland * The exponential backoff starts at 10 seconds and tops out at one
1485 1.96 dholland * hour; see above.
1486 1.68 dholland */
1487 1.69 dholland static void
1488 1.68 dholland checkwork(void)
1489 1.68 dholland {
1490 1.78 dholland struct domain *dom;
1491 1.68 dholland time_t t;
1492 1.68 dholland
1493 1.68 dholland check = 0;
1494 1.68 dholland
1495 1.68 dholland (void)time(&t);
1496 1.78 dholland for (dom = domains; dom != NULL; dom = dom->dom_next) {
1497 1.96 dholland if (dom->dom_checktime >= t) {
1498 1.96 dholland continue;
1499 1.96 dholland }
1500 1.96 dholland switch (dom->dom_state) {
1501 1.96 dholland case DOM_NEW:
1502 1.96 dholland /* XXX should be a timeout for this state */
1503 1.96 dholland dom->dom_checktime = t + 5;
1504 1.96 dholland (void)nag_servers(dom);
1505 1.96 dholland break;
1506 1.96 dholland
1507 1.96 dholland case DOM_ALIVE:
1508 1.96 dholland dom->dom_state = DOM_PINGING;
1509 1.96 dholland dom->dom_checktime = t + 5;
1510 1.96 dholland (void)ping(dom);
1511 1.96 dholland break;
1512 1.96 dholland
1513 1.96 dholland case DOM_PINGING:
1514 1.96 dholland dom->dom_state = DOM_LOST;
1515 1.96 dholland dom->dom_losttime = t;
1516 1.78 dholland dom->dom_checktime = t + 5;
1517 1.96 dholland (void)nag_servers(dom);
1518 1.96 dholland break;
1519 1.96 dholland
1520 1.96 dholland case DOM_LOST:
1521 1.96 dholland if (t > dom->dom_losttime + 60) {
1522 1.96 dholland dom->dom_state = DOM_DEAD;
1523 1.96 dholland dom->dom_backofftime = 10;
1524 1.96 dholland }
1525 1.96 dholland dom->dom_checktime = t + 5;
1526 1.96 dholland (void)nag_servers(dom);
1527 1.96 dholland break;
1528 1.96 dholland
1529 1.96 dholland case DOM_DEAD:
1530 1.96 dholland dom->dom_checktime = t + dom->dom_backofftime;
1531 1.96 dholland backoff(&dom->dom_backofftime);
1532 1.96 dholland (void)nag_servers(dom);
1533 1.96 dholland break;
1534 1.68 dholland }
1535 1.96 dholland /* re-fetch the time in case we hung sending packets */
1536 1.96 dholland (void)time(&t);
1537 1.68 dholland }
1538 1.68 dholland }
1539 1.68 dholland
1540 1.97 dholland /*
1541 1.97 dholland * Process a hangup signal.
1542 1.97 dholland *
1543 1.97 dholland * Do an extra nag_servers() for any domains that are DEAD. This way
1544 1.97 dholland * if you know things are back up you can restore service by sending
1545 1.97 dholland * ypbind a SIGHUP rather than waiting for the timeout period.
1546 1.97 dholland */
1547 1.97 dholland static void
1548 1.97 dholland dohup(void)
1549 1.97 dholland {
1550 1.97 dholland struct domain *dom;
1551 1.97 dholland
1552 1.97 dholland hupped = 0;
1553 1.97 dholland for (dom = domains; dom != NULL; dom = dom->dom_next) {
1554 1.97 dholland if (dom->dom_state == DOM_DEAD) {
1555 1.97 dholland (void)nag_servers(dom);
1556 1.97 dholland }
1557 1.97 dholland }
1558 1.97 dholland }
1559 1.97 dholland
1560 1.97 dholland /*
1561 1.97 dholland * Receive a hangup signal.
1562 1.97 dholland */
1563 1.97 dholland static void
1564 1.97 dholland hup(int __unused sig)
1565 1.97 dholland {
1566 1.97 dholland hupped = 1;
1567 1.97 dholland }
1568 1.97 dholland
1569 1.97 dholland /*
1570 1.97 dholland * Initialize hangup processing.
1571 1.97 dholland */
1572 1.97 dholland static void
1573 1.97 dholland starthup(void)
1574 1.97 dholland {
1575 1.97 dholland struct sigaction sa;
1576 1.97 dholland
1577 1.97 dholland sa.sa_handler = hup;
1578 1.97 dholland sigemptyset(&sa.sa_mask);
1579 1.97 dholland sa.sa_flags = SA_RESTART;
1580 1.97 dholland if (sigaction(SIGHUP, &sa, NULL) == -1) {
1581 1.97 dholland err(1, "sigaction");
1582 1.97 dholland }
1583 1.97 dholland }
1584 1.97 dholland
1585 1.68 dholland ////////////////////////////////////////////////////////////
1586 1.68 dholland // main
1587 1.68 dholland
1588 1.95 dholland /*
1589 1.95 dholland * Usage message.
1590 1.95 dholland */
1591 1.89 joerg __dead static void
1592 1.68 dholland usage(void)
1593 1.68 dholland {
1594 1.68 dholland const char *opt = "";
1595 1.68 dholland #ifdef DEBUG
1596 1.68 dholland opt = " [-d]";
1597 1.68 dholland #endif
1598 1.68 dholland
1599 1.68 dholland (void)fprintf(stderr,
1600 1.68 dholland "Usage: %s [-broadcast] [-insecure] [-ypset] [-ypsetme]%s\n",
1601 1.68 dholland getprogname(), opt);
1602 1.68 dholland exit(1);
1603 1.68 dholland }
1604 1.68 dholland
1605 1.95 dholland /*
1606 1.95 dholland * Main.
1607 1.95 dholland */
1608 1.68 dholland int
1609 1.68 dholland main(int argc, char *argv[])
1610 1.68 dholland {
1611 1.68 dholland struct timeval tv;
1612 1.68 dholland fd_set fdsr;
1613 1.68 dholland int width, lockfd;
1614 1.93 dholland int started = 0;
1615 1.91 dholland char *domainname;
1616 1.10 deraadt
1617 1.68 dholland setprogname(argv[0]);
1618 1.10 deraadt
1619 1.95 dholland /*
1620 1.95 dholland * Process arguments.
1621 1.95 dholland */
1622 1.95 dholland
1623 1.82 dholland default_ypbindmode = YPBIND_DIRECT;
1624 1.68 dholland while (--argc) {
1625 1.68 dholland ++argv;
1626 1.81 dholland if (!strcmp("-insecure", *argv)) {
1627 1.68 dholland insecure = 1;
1628 1.81 dholland } else if (!strcmp("-ypset", *argv)) {
1629 1.81 dholland allow_any_ypset = 1;
1630 1.81 dholland allow_local_ypset = 1;
1631 1.81 dholland } else if (!strcmp("-ypsetme", *argv)) {
1632 1.81 dholland allow_any_ypset = 0;
1633 1.81 dholland allow_local_ypset = 1;
1634 1.81 dholland } else if (!strcmp("-broadcast", *argv)) {
1635 1.82 dholland default_ypbindmode = YPBIND_BROADCAST;
1636 1.68 dholland #ifdef DEBUG
1637 1.81 dholland } else if (!strcmp("-d", *argv)) {
1638 1.81 dholland debug = 1;
1639 1.68 dholland #endif
1640 1.81 dholland } else {
1641 1.68 dholland usage();
1642 1.81 dholland }
1643 1.10 deraadt }
1644 1.10 deraadt
1645 1.95 dholland /*
1646 1.95 dholland * Look up the name of the default domain.
1647 1.95 dholland */
1648 1.95 dholland
1649 1.94 dholland (void)yp_get_default_domain(&domainname);
1650 1.94 dholland if (domainname[0] == '\0')
1651 1.94 dholland errx(1, "Domainname not set. Aborting.");
1652 1.94 dholland if (_yp_invalid_domain(domainname))
1653 1.94 dholland errx(1, "Invalid domainname: %s", domainname);
1654 1.94 dholland
1655 1.95 dholland /*
1656 1.95 dholland * Start things up.
1657 1.95 dholland */
1658 1.95 dholland
1659 1.95 dholland /* Open the system log. */
1660 1.68 dholland openlog("ypbind", LOG_PERROR | LOG_PID, LOG_DAEMON);
1661 1.10 deraadt
1662 1.95 dholland /* Acquire /var/run/ypbind.lock. */
1663 1.73 dholland lockfd = open_locked(_PATH_YPBIND_LOCK, O_CREAT|O_RDWR|O_TRUNC, 0644);
1664 1.68 dholland if (lockfd == -1)
1665 1.68 dholland err(1, "Cannot create %s", _PATH_YPBIND_LOCK);
1666 1.14 cgd
1667 1.97 dholland /* Accept hangups. */
1668 1.97 dholland starthup();
1669 1.97 dholland
1670 1.95 dholland /* Initialize sunrpc stuff. */
1671 1.74 dholland sunrpc_setup();
1672 1.1 deraadt
1673 1.95 dholland /* Clean out BINDINGDIR, deleting all existing (now stale) bindings */
1674 1.68 dholland if (purge_bindingdir(BINDINGDIR) < 0)
1675 1.95 dholland errx(1, "Unable to purge old bindings from %s", BINDINGDIR);
1676 1.95 dholland
1677 1.95 dholland /*
1678 1.95 dholland * We start with one binding, for the default domain. It starts
1679 1.95 dholland * out "unsuccessful".
1680 1.95 dholland *
1681 1.95 dholland * XXX: domain_create adds the new domain to 'domains' (the
1682 1.95 dholland * global linked list) and therefore we shouldn't assign
1683 1.95 dholland * 'domains' again on return.
1684 1.95 dholland */
1685 1.6 deraadt
1686 1.77 dholland domains = domain_create(domainname);
1687 1.95 dholland
1688 1.95 dholland /*
1689 1.95 dholland * Delete the lock for the default domain again, just in case something
1690 1.95 dholland * magically caused it to appear since purge_bindingdir() was called.
1691 1.95 dholland * XXX: this is useless and redundant; remove it.
1692 1.95 dholland */
1693 1.77 dholland removelock(domains);
1694 1.6 deraadt
1695 1.95 dholland /*
1696 1.95 dholland * Main loop. Wake up at least once a second and check for
1697 1.95 dholland * timer-based work to do (checkwork) and also handle incoming
1698 1.95 dholland * responses from ypservers and any RPCs made to the ypbind
1699 1.95 dholland * service.
1700 1.95 dholland *
1701 1.95 dholland * There are two sockets used for ypserver traffic: one for
1702 1.95 dholland * pings and one for everything else. These call XDR manually
1703 1.95 dholland * for encoding and are *not* dispatched via the sunrpc
1704 1.95 dholland * libraries.
1705 1.95 dholland *
1706 1.95 dholland * The ypbind serivce *is* dispatched via the sunrpc libraries.
1707 1.95 dholland * svc_getreqset() does whatever internal muck and ultimately
1708 1.95 dholland * ypbind service calls arrive at ypbindprog_2().
1709 1.95 dholland */
1710 1.68 dholland checkwork();
1711 1.68 dholland for (;;) {
1712 1.68 dholland width = svc_maxfd;
1713 1.68 dholland if (rpcsock > width)
1714 1.68 dholland width = rpcsock;
1715 1.68 dholland if (pingsock > width)
1716 1.68 dholland width = pingsock;
1717 1.68 dholland width++;
1718 1.68 dholland fdsr = svc_fdset;
1719 1.68 dholland FD_SET(rpcsock, &fdsr);
1720 1.68 dholland FD_SET(pingsock, &fdsr);
1721 1.68 dholland tv.tv_sec = 1;
1722 1.68 dholland tv.tv_usec = 0;
1723 1.20 cgd
1724 1.68 dholland switch (select(width, &fdsr, NULL, NULL, &tv)) {
1725 1.68 dholland case 0:
1726 1.95 dholland /* select timed out - check for timer-based work */
1727 1.97 dholland if (hupped) {
1728 1.97 dholland dohup();
1729 1.97 dholland }
1730 1.68 dholland checkwork();
1731 1.68 dholland break;
1732 1.68 dholland case -1:
1733 1.97 dholland if (hupped) {
1734 1.97 dholland dohup();
1735 1.97 dholland }
1736 1.97 dholland if (errno != EINTR) {
1737 1.97 dholland yp_log(LOG_WARNING, "select: %s",
1738 1.97 dholland strerror(errno));
1739 1.97 dholland }
1740 1.68 dholland break;
1741 1.68 dholland default:
1742 1.97 dholland if (hupped) {
1743 1.97 dholland dohup();
1744 1.97 dholland }
1745 1.95 dholland /* incoming of our own; read it */
1746 1.68 dholland if (FD_ISSET(rpcsock, &fdsr))
1747 1.68 dholland (void)handle_replies();
1748 1.68 dholland if (FD_ISSET(pingsock, &fdsr))
1749 1.68 dholland (void)handle_ping();
1750 1.95 dholland
1751 1.95 dholland /* read any incoming packets for the ypbind service */
1752 1.68 dholland svc_getreqset(&fdsr);
1753 1.95 dholland
1754 1.95 dholland /*
1755 1.95 dholland * Only check for timer-based work if
1756 1.95 dholland * something in the incoming RPC logic said
1757 1.95 dholland * to. This might be just a hack to avoid
1758 1.95 dholland * scanning the list unnecessarily, but I
1759 1.95 dholland * suspect it's also a hack to cover wrong
1760 1.95 dholland * state logic. - dholland 20140609
1761 1.95 dholland */
1762 1.68 dholland if (check)
1763 1.68 dholland checkwork();
1764 1.20 cgd break;
1765 1.68 dholland }
1766 1.20 cgd
1767 1.95 dholland /*
1768 1.95 dholland * Defer daemonizing until the default domain binds
1769 1.95 dholland * successfully. XXX: there seems to be no timeout
1770 1.95 dholland * on this, which means that if the default domain
1771 1.95 dholland * is dead upstream boot will hang indefinitely.
1772 1.95 dholland */
1773 1.96 dholland if (!started && domains->dom_state == DOM_ALIVE) {
1774 1.93 dholland started = 1;
1775 1.68 dholland #ifdef DEBUG
1776 1.68 dholland if (!debug)
1777 1.68 dholland #endif
1778 1.68 dholland (void)daemon(0, 0);
1779 1.68 dholland (void)pidfile(NULL);
1780 1.68 dholland }
1781 1.68 dholland }
1782 1.1 deraadt }
1783