ypbind.c revision 1.98 1 1.98 dholland /* $NetBSD: ypbind.c,v 1.98 2014/06/10 17:19:48 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.98 dholland __RCSID("$NetBSD: ypbind.c,v 1.98 2014/06/10 17:19:48 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.98 dholland if (is_ypset == 0) {
632 1.98 dholland yp_log(LOG_NOTICE, "Domain %s is alive; server %s",
633 1.98 dholland dom->dom_name,
634 1.98 dholland inet_ntoa(dom->dom_server_addr.sin_addr));
635 1.98 dholland }
636 1.98 dholland
637 1.95 dholland /*
638 1.95 dholland * Generate a new binding file. If this fails, forget about it.
639 1.95 dholland * (But we keep the binding and we'll report it to anyone who
640 1.95 dholland * asks via the ypbind service.) XXX: this will interact badly,
641 1.95 dholland * maybe very badly, with the code in HEURISTIC.
642 1.95 dholland *
643 1.95 dholland * Note that makelock() doesn't log on failure.
644 1.95 dholland */
645 1.95 dholland
646 1.78 dholland if (dom->dom_lockfd != -1)
647 1.78 dholland (void)close(dom->dom_lockfd);
648 1.68 dholland
649 1.78 dholland if ((fd = makelock(dom)) == -1)
650 1.68 dholland return;
651 1.68 dholland
652 1.78 dholland dom->dom_lockfd = fd;
653 1.68 dholland
654 1.68 dholland iov[0].iov_base = &(udptransp->xp_port);
655 1.68 dholland iov[0].iov_len = sizeof udptransp->xp_port;
656 1.68 dholland iov[1].iov_base = &ybr;
657 1.68 dholland iov[1].iov_len = sizeof ybr;
658 1.68 dholland
659 1.68 dholland (void)memset(&ybr, 0, sizeof ybr);
660 1.68 dholland ybr.ypbind_status = YPBIND_SUCC_VAL;
661 1.68 dholland ybr.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr =
662 1.68 dholland raddrp->sin_addr;
663 1.68 dholland ybr.ypbind_respbody.ypbind_bindinfo.ypbind_binding_port =
664 1.68 dholland raddrp->sin_port;
665 1.68 dholland
666 1.78 dholland result = writev(dom->dom_lockfd, iov, 2);
667 1.72 dholland if (result < 0 || (size_t)result != iov[0].iov_len + iov[1].iov_len) {
668 1.72 dholland if (result < 0)
669 1.72 dholland yp_log(LOG_WARNING, "writev: %s", strerror(errno));
670 1.72 dholland else
671 1.72 dholland yp_log(LOG_WARNING, "writev: short count");
672 1.78 dholland (void)close(dom->dom_lockfd);
673 1.78 dholland removelock(dom);
674 1.78 dholland dom->dom_lockfd = -1;
675 1.68 dholland }
676 1.68 dholland }
677 1.68 dholland
678 1.95 dholland /*
679 1.95 dholland * The NULL call: do nothing. This is obliged to exist because of
680 1.95 dholland * sunrpc silliness.
681 1.95 dholland */
682 1.24 christos static void *
683 1.57 christos /*ARGSUSED*/
684 1.47 wiz ypbindproc_null_2(SVCXPRT *transp, void *argp)
685 1.1 deraadt {
686 1.1 deraadt static char res;
687 1.1 deraadt
688 1.65 dholland DPRINTF("ypbindproc_null_2\n");
689 1.36 mrg (void)memset(&res, 0, sizeof(res));
690 1.1 deraadt return (void *)&res;
691 1.1 deraadt }
692 1.1 deraadt
693 1.95 dholland /*
694 1.95 dholland * The DOMAIN call: look up the ypserver for a specified domain.
695 1.95 dholland */
696 1.24 christos static void *
697 1.57 christos /*ARGSUSED*/
698 1.47 wiz ypbindproc_domain_2(SVCXPRT *transp, void *argp)
699 1.1 deraadt {
700 1.1 deraadt static struct ypbind_resp res;
701 1.78 dholland struct domain *dom;
702 1.24 christos char *arg = *(char **) argp;
703 1.9 deraadt time_t now;
704 1.30 lukem int count;
705 1.1 deraadt
706 1.65 dholland DPRINTF("ypbindproc_domain_2 %s\n", arg);
707 1.95 dholland
708 1.95 dholland /* Reject invalid domains. */
709 1.30 lukem if (_yp_invalid_domain(arg))
710 1.30 lukem return NULL;
711 1.30 lukem
712 1.36 mrg (void)memset(&res, 0, sizeof res);
713 1.1 deraadt res.ypbind_status = YPBIND_FAIL_VAL;
714 1.1 deraadt
715 1.95 dholland /*
716 1.95 dholland * Look for the domain. XXX: Behave erratically if we have
717 1.95 dholland * more than 100 domains. The intent here is to avoid allowing
718 1.95 dholland * arbitrary incoming requests to create more than 100
719 1.95 dholland * domains; but this logic means that if we legitimately have
720 1.95 dholland * more than 100 (e.g. via ypset) we'll only actually bind the
721 1.95 dholland * first 100 and the rest will fail. The test on 'count' should
722 1.95 dholland * be moved further down.
723 1.95 dholland */
724 1.78 dholland for (count = 0, dom = domains;
725 1.78 dholland dom != NULL;
726 1.78 dholland dom = dom->dom_next, count++) {
727 1.30 lukem if (count > 100)
728 1.30 lukem return NULL; /* prevent denial of service */
729 1.79 dholland if (!strcmp(dom->dom_name, arg))
730 1.1 deraadt break;
731 1.30 lukem }
732 1.1 deraadt
733 1.95 dholland /*
734 1.95 dholland * If the domain doesn't exist, create it, then fail the call
735 1.95 dholland * because we have no information yet.
736 1.95 dholland *
737 1.95 dholland * Set "check" so that checkwork() will run and look for a
738 1.95 dholland * server.
739 1.95 dholland *
740 1.95 dholland * XXX: like during startup there's a spurious call to
741 1.95 dholland * removelock() after domain_create().
742 1.95 dholland */
743 1.78 dholland if (dom == NULL) {
744 1.78 dholland dom = domain_create(arg);
745 1.78 dholland removelock(dom);
746 1.1 deraadt check++;
747 1.65 dholland DPRINTF("unknown domain %s\n", arg);
748 1.1 deraadt return NULL;
749 1.1 deraadt }
750 1.1 deraadt
751 1.96 dholland if (dom->dom_state == DOM_NEW) {
752 1.96 dholland DPRINTF("new domain %s\n", arg);
753 1.1 deraadt return NULL;
754 1.24 christos }
755 1.1 deraadt
756 1.9 deraadt #ifdef HEURISTIC
757 1.95 dholland /*
758 1.95 dholland * Keep track of the last time we were explicitly asked about
759 1.95 dholland * this domain. If it happens a lot, force a ping. This works
760 1.95 dholland * (or "works") because we only get asked specifically when
761 1.95 dholland * things aren't going; otherwise the client code in libc and
762 1.95 dholland * elsewhere uses the binding file.
763 1.95 dholland *
764 1.95 dholland * Note: HEURISTIC is enabled by default.
765 1.96 dholland *
766 1.96 dholland * dholland 20140609: I think this is part of the mechanism
767 1.96 dholland * that causes ypbind to spam. I'm changing this logic so it
768 1.96 dholland * only triggers when the state is DOM_ALIVE: if the domain
769 1.96 dholland * is new, lost, or dead we shouldn't send more requests than
770 1.96 dholland * the ones already scheduled, and if we're already in the
771 1.96 dholland * middle of pinging there's no point doing it again.
772 1.95 dholland */
773 1.57 christos (void)time(&now);
774 1.96 dholland if (dom->dom_state == DOM_ALIVE && now < dom->dom_asktime + 5) {
775 1.1 deraadt /*
776 1.9 deraadt * Hmm. More than 2 requests in 5 seconds have indicated
777 1.9 deraadt * that my binding is possibly incorrect.
778 1.9 deraadt * Ok, do an immediate poll of the server.
779 1.1 deraadt */
780 1.78 dholland if (dom->dom_checktime >= now) {
781 1.9 deraadt /* don't flood it */
782 1.78 dholland dom->dom_checktime = 0;
783 1.9 deraadt check++;
784 1.9 deraadt }
785 1.1 deraadt }
786 1.78 dholland dom->dom_asktime = now;
787 1.1 deraadt #endif
788 1.1 deraadt
789 1.1 deraadt res.ypbind_status = YPBIND_SUCC_VAL;
790 1.1 deraadt res.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr.s_addr =
791 1.78 dholland dom->dom_server_addr.sin_addr.s_addr;
792 1.1 deraadt res.ypbind_respbody.ypbind_bindinfo.ypbind_binding_port =
793 1.78 dholland dom->dom_server_addr.sin_port;
794 1.79 dholland DPRINTF("domain %s at %s/%d\n", dom->dom_name,
795 1.78 dholland inet_ntoa(dom->dom_server_addr.sin_addr),
796 1.78 dholland ntohs(dom->dom_server_addr.sin_port));
797 1.1 deraadt return &res;
798 1.1 deraadt }
799 1.1 deraadt
800 1.95 dholland /*
801 1.95 dholland * The SETDOM call: ypset.
802 1.95 dholland *
803 1.95 dholland * Unless -ypsetme was given on the command line, this is rejected;
804 1.95 dholland * even then it's only allowed from localhost unless -ypset was
805 1.95 dholland * given on the command line.
806 1.95 dholland *
807 1.95 dholland * Allowing anyone anywhere to ypset you (and therefore provide your
808 1.95 dholland * password file and such) is a horrible thing and it isn't clear to
809 1.95 dholland * me why this functionality even exists.
810 1.95 dholland *
811 1.95 dholland * ypset from localhost has some but limited utility.
812 1.95 dholland */
813 1.24 christos static void *
814 1.47 wiz ypbindproc_setdom_2(SVCXPRT *transp, void *argp)
815 1.1 deraadt {
816 1.24 christos struct ypbind_setdom *sd = argp;
817 1.1 deraadt struct sockaddr_in *fromsin, bindsin;
818 1.13 deraadt static bool_t res;
819 1.1 deraadt
820 1.36 mrg (void)memset(&res, 0, sizeof(res));
821 1.1 deraadt fromsin = svc_getcaller(transp);
822 1.88 dholland DPRINTF("ypbindproc_setdom_2 from %s\n", inet_ntoa(fromsin->sin_addr));
823 1.1 deraadt
824 1.95 dholland /*
825 1.95 dholland * Reject unless enabled.
826 1.95 dholland */
827 1.95 dholland
828 1.81 dholland if (allow_any_ypset) {
829 1.81 dholland /* nothing */
830 1.81 dholland } else if (allow_local_ypset) {
831 1.24 christos if (fromsin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
832 1.81 dholland DPRINTF("ypset denied from %s\n",
833 1.65 dholland inet_ntoa(fromsin->sin_addr));
834 1.24 christos return NULL;
835 1.24 christos }
836 1.81 dholland } else {
837 1.65 dholland DPRINTF("ypset denied\n");
838 1.24 christos return NULL;
839 1.1 deraadt }
840 1.4 deraadt
841 1.95 dholland /* Make a "security" check. */
842 1.24 christos if (ntohs(fromsin->sin_port) >= IPPORT_RESERVED) {
843 1.65 dholland DPRINTF("ypset from unprivileged port denied\n");
844 1.13 deraadt return &res;
845 1.24 christos }
846 1.1 deraadt
847 1.95 dholland /* Ignore requests we don't understand. */
848 1.24 christos if (sd->ypsetdom_vers != YPVERS) {
849 1.65 dholland DPRINTF("ypset with wrong version denied\n");
850 1.13 deraadt return &res;
851 1.24 christos }
852 1.1 deraadt
853 1.95 dholland /*
854 1.95 dholland * Fetch the arguments out of the xdr-decoded blob and call
855 1.95 dholland * rpc_received(), setting FORCE so that the domain will be
856 1.95 dholland * created if we don't already know about it, and also saying
857 1.95 dholland * that it's actually a ypset.
858 1.95 dholland *
859 1.95 dholland * Effectively we're telilng rpc_received() that we got an
860 1.95 dholland * RPC response from the server specified by ypset.
861 1.95 dholland */
862 1.36 mrg (void)memset(&bindsin, 0, sizeof bindsin);
863 1.1 deraadt bindsin.sin_family = AF_INET;
864 1.17 mycroft bindsin.sin_len = sizeof(bindsin);
865 1.24 christos bindsin.sin_addr = sd->ypsetdom_addr;
866 1.24 christos bindsin.sin_port = sd->ypsetdom_port;
867 1.83 dholland rpc_received(sd->ypsetdom_domain, &bindsin, 1, 1);
868 1.24 christos
869 1.85 dholland DPRINTF("ypset to %s for domain %s succeeded\n",
870 1.85 dholland inet_ntoa(bindsin.sin_addr), sd->ypsetdom_domain);
871 1.1 deraadt res = 1;
872 1.13 deraadt return &res;
873 1.1 deraadt }
874 1.1 deraadt
875 1.95 dholland /*
876 1.95 dholland * Dispatcher for the ypbind service.
877 1.95 dholland *
878 1.95 dholland * There are three calls: NULL, which does nothing, DOMAIN, which
879 1.95 dholland * gets the binding for a particular domain, and SETDOM, which
880 1.95 dholland * does ypset.
881 1.95 dholland */
882 1.1 deraadt static void
883 1.47 wiz ypbindprog_2(struct svc_req *rqstp, register SVCXPRT *transp)
884 1.1 deraadt {
885 1.1 deraadt union {
886 1.22 thorpej char ypbindproc_domain_2_arg[YPMAXDOMAIN + 1];
887 1.1 deraadt struct ypbind_setdom ypbindproc_setdom_2_arg;
888 1.56 tron void *alignment;
889 1.1 deraadt } argument;
890 1.1 deraadt struct authunix_parms *creds;
891 1.1 deraadt char *result;
892 1.24 christos xdrproc_t xdr_argument, xdr_result;
893 1.47 wiz void *(*local)(SVCXPRT *, void *);
894 1.1 deraadt
895 1.1 deraadt switch (rqstp->rq_proc) {
896 1.1 deraadt case YPBINDPROC_NULL:
897 1.90 plunky xdr_argument = (xdrproc_t)xdr_void;
898 1.90 plunky xdr_result = (xdrproc_t)xdr_void;
899 1.24 christos local = ypbindproc_null_2;
900 1.1 deraadt break;
901 1.1 deraadt
902 1.1 deraadt case YPBINDPROC_DOMAIN:
903 1.90 plunky xdr_argument = (xdrproc_t)xdr_ypdomain_wrap_string;
904 1.90 plunky xdr_result = (xdrproc_t)xdr_ypbind_resp;
905 1.24 christos local = ypbindproc_domain_2;
906 1.1 deraadt break;
907 1.1 deraadt
908 1.1 deraadt case YPBINDPROC_SETDOM:
909 1.17 mycroft switch (rqstp->rq_cred.oa_flavor) {
910 1.1 deraadt case AUTH_UNIX:
911 1.1 deraadt creds = (struct authunix_parms *)rqstp->rq_clntcred;
912 1.17 mycroft if (creds->aup_uid != 0) {
913 1.1 deraadt svcerr_auth(transp, AUTH_BADCRED);
914 1.1 deraadt return;
915 1.1 deraadt }
916 1.1 deraadt break;
917 1.1 deraadt default:
918 1.1 deraadt svcerr_auth(transp, AUTH_TOOWEAK);
919 1.1 deraadt return;
920 1.1 deraadt }
921 1.1 deraadt
922 1.90 plunky xdr_argument = (xdrproc_t)xdr_ypbind_setdom;
923 1.90 plunky xdr_result = (xdrproc_t)xdr_void;
924 1.24 christos local = ypbindproc_setdom_2;
925 1.1 deraadt break;
926 1.1 deraadt
927 1.1 deraadt default:
928 1.1 deraadt svcerr_noproc(transp);
929 1.1 deraadt return;
930 1.1 deraadt }
931 1.36 mrg (void)memset(&argument, 0, sizeof(argument));
932 1.57 christos if (!svc_getargs(transp, xdr_argument, (caddr_t)(void *)&argument)) {
933 1.1 deraadt svcerr_decode(transp);
934 1.1 deraadt return;
935 1.1 deraadt }
936 1.24 christos result = (*local)(transp, &argument);
937 1.1 deraadt if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
938 1.1 deraadt svcerr_systemerr(transp);
939 1.1 deraadt }
940 1.1 deraadt return;
941 1.1 deraadt }
942 1.1 deraadt
943 1.95 dholland /*
944 1.95 dholland * Set up sunrpc stuff.
945 1.95 dholland *
946 1.95 dholland * This sets up the ypbind service (both TCP and UDP) and also opens
947 1.95 dholland * the sockets we use for talking to ypservers.
948 1.95 dholland */
949 1.74 dholland static void
950 1.74 dholland sunrpc_setup(void)
951 1.74 dholland {
952 1.74 dholland int one;
953 1.74 dholland
954 1.74 dholland (void)pmap_unset(YPBINDPROG, YPBINDVERS);
955 1.74 dholland
956 1.74 dholland udptransp = svcudp_create(RPC_ANYSOCK);
957 1.74 dholland if (udptransp == NULL)
958 1.74 dholland errx(1, "Cannot create udp service.");
959 1.74 dholland
960 1.74 dholland if (!svc_register(udptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
961 1.74 dholland IPPROTO_UDP))
962 1.74 dholland errx(1, "Unable to register (YPBINDPROG, YPBINDVERS, udp).");
963 1.74 dholland
964 1.74 dholland tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0);
965 1.74 dholland if (tcptransp == NULL)
966 1.74 dholland errx(1, "Cannot create tcp service.");
967 1.74 dholland
968 1.74 dholland if (!svc_register(tcptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
969 1.74 dholland IPPROTO_TCP))
970 1.74 dholland errx(1, "Unable to register (YPBINDPROG, YPBINDVERS, tcp).");
971 1.74 dholland
972 1.74 dholland /* XXX use SOCK_STREAM for direct queries? */
973 1.74 dholland if ((rpcsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
974 1.74 dholland err(1, "rpc socket");
975 1.74 dholland if ((pingsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
976 1.74 dholland err(1, "ping socket");
977 1.74 dholland
978 1.74 dholland (void)fcntl(rpcsock, F_SETFL, fcntl(rpcsock, F_GETFL, 0) | FNDELAY);
979 1.74 dholland (void)fcntl(pingsock, F_SETFL, fcntl(pingsock, F_GETFL, 0) | FNDELAY);
980 1.74 dholland
981 1.74 dholland one = 1;
982 1.74 dholland (void)setsockopt(rpcsock, SOL_SOCKET, SO_BROADCAST, &one,
983 1.74 dholland (socklen_t)sizeof(one));
984 1.74 dholland rmtca.prog = YPPROG;
985 1.74 dholland rmtca.vers = YPVERS;
986 1.74 dholland rmtca.proc = YPPROC_DOMAIN_NONACK;
987 1.74 dholland rmtca.xdr_args = NULL; /* set at call time */
988 1.74 dholland rmtca.args_ptr = NULL; /* set at call time */
989 1.74 dholland rmtcr.port_ptr = &rmtcr_port;
990 1.90 plunky rmtcr.xdr_results = (xdrproc_t)xdr_bool;
991 1.74 dholland rmtcr.results_ptr = (caddr_t)(void *)&rmtcr_outval;
992 1.74 dholland }
993 1.74 dholland
994 1.68 dholland ////////////////////////////////////////////////////////////
995 1.68 dholland // operational logic
996 1.68 dholland
997 1.95 dholland /*
998 1.95 dholland * Broadcast an RPC packet to hopefully contact some servers for a
999 1.95 dholland * domain.
1000 1.95 dholland */
1001 1.68 dholland static int
1002 1.68 dholland broadcast(char *buf, int outlen)
1003 1.1 deraadt {
1004 1.68 dholland struct ifaddrs *ifap, *ifa;
1005 1.68 dholland struct sockaddr_in bindsin;
1006 1.68 dholland struct in_addr in;
1007 1.68 dholland
1008 1.68 dholland (void)memset(&bindsin, 0, sizeof bindsin);
1009 1.68 dholland bindsin.sin_family = AF_INET;
1010 1.68 dholland bindsin.sin_len = sizeof(bindsin);
1011 1.68 dholland bindsin.sin_port = htons(PMAPPORT);
1012 1.1 deraadt
1013 1.68 dholland if (getifaddrs(&ifap) != 0) {
1014 1.72 dholland yp_log(LOG_WARNING, "broadcast: getifaddrs: %s",
1015 1.72 dholland strerror(errno));
1016 1.68 dholland return (-1);
1017 1.68 dholland }
1018 1.68 dholland for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
1019 1.68 dholland if (ifa->ifa_addr->sa_family != AF_INET)
1020 1.68 dholland continue;
1021 1.68 dholland if ((ifa->ifa_flags & IFF_UP) == 0)
1022 1.68 dholland continue;
1023 1.1 deraadt
1024 1.68 dholland switch (ifa->ifa_flags & (IFF_LOOPBACK | IFF_BROADCAST)) {
1025 1.68 dholland case IFF_BROADCAST:
1026 1.68 dholland if (!ifa->ifa_broadaddr)
1027 1.68 dholland continue;
1028 1.68 dholland if (ifa->ifa_broadaddr->sa_family != AF_INET)
1029 1.68 dholland continue;
1030 1.68 dholland in = ((struct sockaddr_in *)(void *)ifa->ifa_broadaddr)->sin_addr;
1031 1.68 dholland break;
1032 1.68 dholland case IFF_LOOPBACK:
1033 1.68 dholland in = ((struct sockaddr_in *)(void *)ifa->ifa_addr)->sin_addr;
1034 1.68 dholland break;
1035 1.68 dholland default:
1036 1.68 dholland continue;
1037 1.68 dholland }
1038 1.27 thorpej
1039 1.68 dholland bindsin.sin_addr = in;
1040 1.68 dholland DPRINTF("broadcast %x\n", bindsin.sin_addr.s_addr);
1041 1.68 dholland if (sendto(rpcsock, buf, outlen, 0,
1042 1.68 dholland (struct sockaddr *)(void *)&bindsin,
1043 1.68 dholland (socklen_t)bindsin.sin_len) == -1)
1044 1.72 dholland yp_log(LOG_WARNING, "broadcast: sendto: %s",
1045 1.72 dholland strerror(errno));
1046 1.1 deraadt }
1047 1.68 dholland freeifaddrs(ifap);
1048 1.68 dholland return (0);
1049 1.68 dholland }
1050 1.1 deraadt
1051 1.95 dholland /*
1052 1.95 dholland * Send an RPC packet to all the configured (in /var/yp/foo.ypservers)
1053 1.95 dholland * servers for a domain.
1054 1.95 dholland *
1055 1.95 dholland * XXX: we should read and parse the file up front and reread it only
1056 1.95 dholland * if it changes.
1057 1.95 dholland */
1058 1.68 dholland static int
1059 1.84 dholland direct(char *buf, int outlen, struct domain *dom)
1060 1.68 dholland {
1061 1.71 dholland const char *path;
1062 1.68 dholland char line[_POSIX2_LINE_MAX];
1063 1.68 dholland char *p;
1064 1.68 dholland struct hostent *hp;
1065 1.68 dholland struct sockaddr_in bindsin;
1066 1.68 dholland int i, count = 0;
1067 1.37 bouyer
1068 1.84 dholland /*
1069 1.84 dholland * XXX what happens if someone's editor unlinks and replaces
1070 1.84 dholland * the servers file?
1071 1.84 dholland */
1072 1.84 dholland
1073 1.84 dholland if (dom->dom_serversfile != NULL) {
1074 1.84 dholland rewind(dom->dom_serversfile);
1075 1.84 dholland } else {
1076 1.84 dholland path = ypservers_filename(dom->dom_name);
1077 1.84 dholland dom->dom_serversfile = fopen(path, "r");
1078 1.84 dholland if (dom->dom_serversfile == NULL) {
1079 1.84 dholland /*
1080 1.84 dholland * XXX there should be a time restriction on
1081 1.84 dholland * this (and/or on trying the open) so we
1082 1.84 dholland * don't flood the log. Or should we fall back
1083 1.84 dholland * to broadcast mode?
1084 1.84 dholland */
1085 1.84 dholland yp_log(LOG_ERR, "%s: %s", path,
1086 1.72 dholland strerror(errno));
1087 1.84 dholland return -1;
1088 1.37 bouyer }
1089 1.27 thorpej }
1090 1.27 thorpej
1091 1.57 christos (void)memset(&bindsin, 0, sizeof bindsin);
1092 1.27 thorpej bindsin.sin_family = AF_INET;
1093 1.27 thorpej bindsin.sin_len = sizeof(bindsin);
1094 1.27 thorpej bindsin.sin_port = htons(PMAPPORT);
1095 1.27 thorpej
1096 1.84 dholland while (fgets(line, (int)sizeof(line), dom->dom_serversfile) != NULL) {
1097 1.27 thorpej /* skip lines that are too big */
1098 1.27 thorpej p = strchr(line, '\n');
1099 1.27 thorpej if (p == NULL) {
1100 1.27 thorpej int c;
1101 1.27 thorpej
1102 1.84 dholland while ((c = getc(dom->dom_serversfile)) != '\n' && c != EOF)
1103 1.27 thorpej ;
1104 1.27 thorpej continue;
1105 1.27 thorpej }
1106 1.27 thorpej *p = '\0';
1107 1.27 thorpej p = line;
1108 1.53 dsl while (isspace((unsigned char)*p))
1109 1.27 thorpej p++;
1110 1.27 thorpej if (*p == '#')
1111 1.27 thorpej continue;
1112 1.27 thorpej hp = gethostbyname(p);
1113 1.27 thorpej if (!hp) {
1114 1.45 lukem yp_log(LOG_WARNING, "%s: %s", p, hstrerror(h_errno));
1115 1.27 thorpej continue;
1116 1.27 thorpej }
1117 1.27 thorpej /* step through all addresses in case first is unavailable */
1118 1.27 thorpej for (i = 0; hp->h_addr_list[i]; i++) {
1119 1.57 christos (void)memcpy(&bindsin.sin_addr, hp->h_addr_list[0],
1120 1.27 thorpej hp->h_length);
1121 1.27 thorpej if (sendto(rpcsock, buf, outlen, 0,
1122 1.57 christos (struct sockaddr *)(void *)&bindsin,
1123 1.57 christos (socklen_t)sizeof(bindsin)) < 0) {
1124 1.72 dholland yp_log(LOG_WARNING, "direct: sendto: %s",
1125 1.72 dholland strerror(errno));
1126 1.27 thorpej continue;
1127 1.27 thorpej } else
1128 1.27 thorpej count++;
1129 1.27 thorpej }
1130 1.27 thorpej }
1131 1.37 bouyer if (!count) {
1132 1.72 dholland yp_log(LOG_WARNING, "No contactable servers found in %s",
1133 1.84 dholland ypservers_filename(dom->dom_name));
1134 1.37 bouyer return -1;
1135 1.37 bouyer }
1136 1.27 thorpej return 0;
1137 1.27 thorpej }
1138 1.27 thorpej
1139 1.95 dholland /*
1140 1.95 dholland * Send an RPC packet to the server that's been selected with ypset.
1141 1.95 dholland * (This is only used when in broadcast mode and when ypset is
1142 1.95 dholland * allowed.)
1143 1.95 dholland */
1144 1.27 thorpej static int
1145 1.78 dholland direct_set(char *buf, int outlen, struct domain *dom)
1146 1.27 thorpej {
1147 1.27 thorpej struct sockaddr_in bindsin;
1148 1.27 thorpej char path[MAXPATHLEN];
1149 1.27 thorpej struct iovec iov[2];
1150 1.27 thorpej struct ypbind_resp ybr;
1151 1.27 thorpej SVCXPRT dummy_svc;
1152 1.57 christos int fd;
1153 1.57 christos ssize_t bytes;
1154 1.27 thorpej
1155 1.27 thorpej /*
1156 1.27 thorpej * Gack, we lose if binding file went away. We reset
1157 1.27 thorpej * "been_set" if this happens, otherwise we'll never
1158 1.27 thorpej * bind again.
1159 1.27 thorpej */
1160 1.57 christos (void)snprintf(path, sizeof(path), "%s/%s.%ld", BINDINGDIR,
1161 1.79 dholland dom->dom_name, dom->dom_vers);
1162 1.27 thorpej
1163 1.73 dholland fd = open_locked(path, O_RDONLY, 0644);
1164 1.73 dholland if (fd == -1) {
1165 1.72 dholland yp_log(LOG_WARNING, "%s: %s", path, strerror(errno));
1166 1.83 dholland dom->dom_been_ypset = 0;
1167 1.27 thorpej return -1;
1168 1.27 thorpej }
1169 1.27 thorpej
1170 1.27 thorpej /* Read the binding file... */
1171 1.57 christos iov[0].iov_base = &(dummy_svc.xp_port);
1172 1.27 thorpej iov[0].iov_len = sizeof(dummy_svc.xp_port);
1173 1.57 christos iov[1].iov_base = &ybr;
1174 1.27 thorpej iov[1].iov_len = sizeof(ybr);
1175 1.27 thorpej bytes = readv(fd, iov, 2);
1176 1.27 thorpej (void)close(fd);
1177 1.72 dholland if (bytes <0 || (size_t)bytes != (iov[0].iov_len + iov[1].iov_len)) {
1178 1.27 thorpej /* Binding file corrupt? */
1179 1.72 dholland if (bytes < 0)
1180 1.72 dholland yp_log(LOG_WARNING, "%s: %s", path, strerror(errno));
1181 1.72 dholland else
1182 1.72 dholland yp_log(LOG_WARNING, "%s: short read", path);
1183 1.83 dholland dom->dom_been_ypset = 0;
1184 1.27 thorpej return -1;
1185 1.68 dholland }
1186 1.68 dholland
1187 1.68 dholland bindsin.sin_addr =
1188 1.68 dholland ybr.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr;
1189 1.68 dholland
1190 1.68 dholland if (sendto(rpcsock, buf, outlen, 0,
1191 1.68 dholland (struct sockaddr *)(void *)&bindsin,
1192 1.68 dholland (socklen_t)sizeof(bindsin)) < 0) {
1193 1.72 dholland yp_log(LOG_WARNING, "direct_set: sendto: %s", strerror(errno));
1194 1.68 dholland return -1;
1195 1.68 dholland }
1196 1.68 dholland
1197 1.68 dholland return 0;
1198 1.68 dholland }
1199 1.68 dholland
1200 1.95 dholland /*
1201 1.95 dholland * Receive and dispatch packets on the general RPC socket.
1202 1.95 dholland */
1203 1.68 dholland static enum clnt_stat
1204 1.68 dholland handle_replies(void)
1205 1.68 dholland {
1206 1.68 dholland char buf[BUFSIZE];
1207 1.68 dholland socklen_t fromlen;
1208 1.68 dholland ssize_t inlen;
1209 1.78 dholland struct domain *dom;
1210 1.68 dholland struct sockaddr_in raddr;
1211 1.68 dholland struct rpc_msg msg;
1212 1.68 dholland XDR xdr;
1213 1.68 dholland
1214 1.68 dholland recv_again:
1215 1.68 dholland DPRINTF("handle_replies receiving\n");
1216 1.68 dholland (void)memset(&xdr, 0, sizeof(xdr));
1217 1.68 dholland (void)memset(&msg, 0, sizeof(msg));
1218 1.68 dholland msg.acpted_rply.ar_verf = _null_auth;
1219 1.68 dholland msg.acpted_rply.ar_results.where = (caddr_t)(void *)&rmtcr;
1220 1.90 plunky msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_rmtcallres;
1221 1.68 dholland
1222 1.68 dholland try_again:
1223 1.68 dholland fromlen = sizeof(struct sockaddr);
1224 1.68 dholland inlen = recvfrom(rpcsock, buf, sizeof buf, 0,
1225 1.68 dholland (struct sockaddr *)(void *)&raddr, &fromlen);
1226 1.68 dholland if (inlen < 0) {
1227 1.68 dholland if (errno == EINTR)
1228 1.68 dholland goto try_again;
1229 1.68 dholland DPRINTF("handle_replies: recvfrom failed (%s)\n",
1230 1.68 dholland strerror(errno));
1231 1.68 dholland return RPC_CANTRECV;
1232 1.68 dholland }
1233 1.68 dholland if ((size_t)inlen < sizeof(uint32_t))
1234 1.68 dholland goto recv_again;
1235 1.27 thorpej
1236 1.68 dholland /*
1237 1.68 dholland * see if reply transaction id matches sent id.
1238 1.68 dholland * If so, decode the results.
1239 1.68 dholland */
1240 1.68 dholland xdrmem_create(&xdr, buf, (unsigned)inlen, XDR_DECODE);
1241 1.68 dholland if (xdr_replymsg(&xdr, &msg)) {
1242 1.68 dholland if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
1243 1.68 dholland (msg.acpted_rply.ar_stat == SUCCESS)) {
1244 1.68 dholland raddr.sin_port = htons((uint16_t)rmtcr_port);
1245 1.78 dholland dom = domain_find(msg.rm_xid);
1246 1.78 dholland if (dom != NULL)
1247 1.83 dholland rpc_received(dom->dom_name, &raddr, 0, 0);
1248 1.68 dholland }
1249 1.27 thorpej }
1250 1.68 dholland xdr.x_op = XDR_FREE;
1251 1.90 plunky msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
1252 1.68 dholland xdr_destroy(&xdr);
1253 1.27 thorpej
1254 1.68 dholland return RPC_SUCCESS;
1255 1.1 deraadt }
1256 1.1 deraadt
1257 1.95 dholland /*
1258 1.95 dholland * Receive and dispatch packets on the ping socket.
1259 1.95 dholland */
1260 1.24 christos static enum clnt_stat
1261 1.68 dholland handle_ping(void)
1262 1.1 deraadt {
1263 1.24 christos char buf[BUFSIZE];
1264 1.54 mrg socklen_t fromlen;
1265 1.57 christos ssize_t inlen;
1266 1.78 dholland struct domain *dom;
1267 1.1 deraadt struct sockaddr_in raddr;
1268 1.1 deraadt struct rpc_msg msg;
1269 1.1 deraadt XDR xdr;
1270 1.68 dholland bool_t res;
1271 1.1 deraadt
1272 1.1 deraadt recv_again:
1273 1.68 dholland DPRINTF("handle_ping receiving\n");
1274 1.36 mrg (void)memset(&xdr, 0, sizeof(xdr));
1275 1.36 mrg (void)memset(&msg, 0, sizeof(msg));
1276 1.1 deraadt msg.acpted_rply.ar_verf = _null_auth;
1277 1.68 dholland msg.acpted_rply.ar_results.where = (caddr_t)(void *)&res;
1278 1.90 plunky msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_bool;
1279 1.1 deraadt
1280 1.1 deraadt try_again:
1281 1.68 dholland fromlen = sizeof (struct sockaddr);
1282 1.68 dholland inlen = recvfrom(pingsock, buf, sizeof buf, 0,
1283 1.68 dholland (struct sockaddr *)(void *)&raddr, &fromlen);
1284 1.17 mycroft if (inlen < 0) {
1285 1.17 mycroft if (errno == EINTR)
1286 1.1 deraadt goto try_again;
1287 1.68 dholland DPRINTF("handle_ping: recvfrom failed (%s)\n",
1288 1.65 dholland strerror(errno));
1289 1.1 deraadt return RPC_CANTRECV;
1290 1.1 deraadt }
1291 1.64 dholland if ((size_t)inlen < sizeof(uint32_t))
1292 1.1 deraadt goto recv_again;
1293 1.1 deraadt
1294 1.1 deraadt /*
1295 1.1 deraadt * see if reply transaction id matches sent id.
1296 1.1 deraadt * If so, decode the results.
1297 1.1 deraadt */
1298 1.64 dholland xdrmem_create(&xdr, buf, (unsigned)inlen, XDR_DECODE);
1299 1.17 mycroft if (xdr_replymsg(&xdr, &msg)) {
1300 1.17 mycroft if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
1301 1.17 mycroft (msg.acpted_rply.ar_stat == SUCCESS)) {
1302 1.78 dholland dom = domain_find(msg.rm_xid);
1303 1.78 dholland if (dom != NULL)
1304 1.83 dholland rpc_received(dom->dom_name, &raddr, 0, 0);
1305 1.1 deraadt }
1306 1.1 deraadt }
1307 1.1 deraadt xdr.x_op = XDR_FREE;
1308 1.90 plunky msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
1309 1.1 deraadt xdr_destroy(&xdr);
1310 1.1 deraadt
1311 1.1 deraadt return RPC_SUCCESS;
1312 1.1 deraadt }
1313 1.1 deraadt
1314 1.95 dholland /*
1315 1.95 dholland * Contact all known servers for a domain in the hopes that one of
1316 1.95 dholland * them's awake. Also, if we previously had a binding but it timed
1317 1.95 dholland * out, try the portmapper on that host in case ypserv moved ports for
1318 1.95 dholland * some reason.
1319 1.95 dholland *
1320 1.95 dholland * As a side effect, wipe out any existing binding file.
1321 1.95 dholland */
1322 1.68 dholland static int
1323 1.78 dholland nag_servers(struct domain *dom)
1324 1.68 dholland {
1325 1.79 dholland char *dom_name = dom->dom_name;
1326 1.68 dholland struct rpc_msg msg;
1327 1.68 dholland char buf[BUFSIZE];
1328 1.68 dholland enum clnt_stat st;
1329 1.68 dholland int outlen;
1330 1.68 dholland AUTH *rpcua;
1331 1.68 dholland XDR xdr;
1332 1.68 dholland
1333 1.68 dholland DPRINTF("nag_servers\n");
1334 1.90 plunky rmtca.xdr_args = (xdrproc_t)xdr_ypdomain_wrap_string;
1335 1.78 dholland rmtca.args_ptr = (caddr_t)(void *)&dom_name;
1336 1.68 dholland
1337 1.68 dholland (void)memset(&xdr, 0, sizeof xdr);
1338 1.68 dholland (void)memset(&msg, 0, sizeof msg);
1339 1.68 dholland
1340 1.68 dholland rpcua = authunix_create_default();
1341 1.68 dholland if (rpcua == NULL) {
1342 1.68 dholland DPRINTF("cannot get unix auth\n");
1343 1.68 dholland return RPC_SYSTEMERROR;
1344 1.68 dholland }
1345 1.68 dholland msg.rm_direction = CALL;
1346 1.68 dholland msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
1347 1.68 dholland msg.rm_call.cb_prog = PMAPPROG;
1348 1.68 dholland msg.rm_call.cb_vers = PMAPVERS;
1349 1.68 dholland msg.rm_call.cb_proc = PMAPPROC_CALLIT;
1350 1.68 dholland msg.rm_call.cb_cred = rpcua->ah_cred;
1351 1.68 dholland msg.rm_call.cb_verf = rpcua->ah_verf;
1352 1.68 dholland
1353 1.78 dholland msg.rm_xid = dom->dom_xid;
1354 1.68 dholland xdrmem_create(&xdr, buf, (unsigned)sizeof(buf), XDR_ENCODE);
1355 1.68 dholland if (!xdr_callmsg(&xdr, &msg)) {
1356 1.68 dholland st = RPC_CANTENCODEARGS;
1357 1.68 dholland AUTH_DESTROY(rpcua);
1358 1.68 dholland return st;
1359 1.68 dholland }
1360 1.68 dholland if (!xdr_rmtcall_args(&xdr, &rmtca)) {
1361 1.68 dholland st = RPC_CANTENCODEARGS;
1362 1.68 dholland AUTH_DESTROY(rpcua);
1363 1.68 dholland return st;
1364 1.68 dholland }
1365 1.68 dholland outlen = (int)xdr_getpos(&xdr);
1366 1.68 dholland xdr_destroy(&xdr);
1367 1.68 dholland if (outlen < 1) {
1368 1.68 dholland st = RPC_CANTENCODEARGS;
1369 1.68 dholland AUTH_DESTROY(rpcua);
1370 1.68 dholland return st;
1371 1.68 dholland }
1372 1.68 dholland AUTH_DESTROY(rpcua);
1373 1.68 dholland
1374 1.78 dholland if (dom->dom_lockfd != -1) {
1375 1.78 dholland (void)close(dom->dom_lockfd);
1376 1.78 dholland dom->dom_lockfd = -1;
1377 1.78 dholland removelock(dom);
1378 1.68 dholland }
1379 1.68 dholland
1380 1.96 dholland if (dom->dom_state == DOM_PINGING || dom->dom_state == DOM_LOST) {
1381 1.68 dholland /*
1382 1.68 dholland * This resolves the following situation:
1383 1.68 dholland * ypserver on other subnet was once bound,
1384 1.68 dholland * but rebooted and is now using a different port
1385 1.68 dholland */
1386 1.68 dholland struct sockaddr_in bindsin;
1387 1.68 dholland
1388 1.68 dholland (void)memset(&bindsin, 0, sizeof bindsin);
1389 1.68 dholland bindsin.sin_family = AF_INET;
1390 1.68 dholland bindsin.sin_len = sizeof(bindsin);
1391 1.68 dholland bindsin.sin_port = htons(PMAPPORT);
1392 1.78 dholland bindsin.sin_addr = dom->dom_server_addr.sin_addr;
1393 1.68 dholland
1394 1.68 dholland if (sendto(rpcsock, buf, outlen, 0,
1395 1.68 dholland (struct sockaddr *)(void *)&bindsin,
1396 1.68 dholland (socklen_t)sizeof bindsin) == -1)
1397 1.72 dholland yp_log(LOG_WARNING, "nag_servers: sendto: %s",
1398 1.72 dholland strerror(errno));
1399 1.68 dholland }
1400 1.68 dholland
1401 1.82 dholland switch (dom->dom_ypbindmode) {
1402 1.81 dholland case YPBIND_BROADCAST:
1403 1.83 dholland if (dom->dom_been_ypset) {
1404 1.78 dholland return direct_set(buf, outlen, dom);
1405 1.81 dholland }
1406 1.68 dholland return broadcast(buf, outlen);
1407 1.68 dholland
1408 1.68 dholland case YPBIND_DIRECT:
1409 1.84 dholland return direct(buf, outlen, dom);
1410 1.68 dholland }
1411 1.68 dholland /*NOTREACHED*/
1412 1.68 dholland return -1;
1413 1.68 dholland }
1414 1.68 dholland
1415 1.95 dholland /*
1416 1.95 dholland * Send a ping message to a domain's current ypserver.
1417 1.95 dholland */
1418 1.69 dholland static int
1419 1.78 dholland ping(struct domain *dom)
1420 1.10 deraadt {
1421 1.79 dholland char *dom_name = dom->dom_name;
1422 1.68 dholland struct rpc_msg msg;
1423 1.24 christos char buf[BUFSIZE];
1424 1.68 dholland enum clnt_stat st;
1425 1.68 dholland int outlen;
1426 1.68 dholland AUTH *rpcua;
1427 1.68 dholland XDR xdr;
1428 1.68 dholland
1429 1.68 dholland (void)memset(&xdr, 0, sizeof xdr);
1430 1.68 dholland (void)memset(&msg, 0, sizeof msg);
1431 1.68 dholland
1432 1.68 dholland rpcua = authunix_create_default();
1433 1.68 dholland if (rpcua == NULL) {
1434 1.68 dholland DPRINTF("cannot get unix auth\n");
1435 1.68 dholland return RPC_SYSTEMERROR;
1436 1.68 dholland }
1437 1.68 dholland
1438 1.68 dholland msg.rm_direction = CALL;
1439 1.68 dholland msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
1440 1.68 dholland msg.rm_call.cb_prog = YPPROG;
1441 1.68 dholland msg.rm_call.cb_vers = YPVERS;
1442 1.68 dholland msg.rm_call.cb_proc = YPPROC_DOMAIN_NONACK;
1443 1.68 dholland msg.rm_call.cb_cred = rpcua->ah_cred;
1444 1.68 dholland msg.rm_call.cb_verf = rpcua->ah_verf;
1445 1.68 dholland
1446 1.78 dholland msg.rm_xid = dom->dom_xid;
1447 1.68 dholland xdrmem_create(&xdr, buf, (unsigned)sizeof(buf), XDR_ENCODE);
1448 1.68 dholland if (!xdr_callmsg(&xdr, &msg)) {
1449 1.68 dholland st = RPC_CANTENCODEARGS;
1450 1.68 dholland AUTH_DESTROY(rpcua);
1451 1.68 dholland return st;
1452 1.68 dholland }
1453 1.78 dholland if (!xdr_ypdomain_wrap_string(&xdr, &dom_name)) {
1454 1.68 dholland st = RPC_CANTENCODEARGS;
1455 1.68 dholland AUTH_DESTROY(rpcua);
1456 1.68 dholland return st;
1457 1.68 dholland }
1458 1.68 dholland outlen = (int)xdr_getpos(&xdr);
1459 1.68 dholland xdr_destroy(&xdr);
1460 1.68 dholland if (outlen < 1) {
1461 1.68 dholland st = RPC_CANTENCODEARGS;
1462 1.68 dholland AUTH_DESTROY(rpcua);
1463 1.68 dholland return st;
1464 1.68 dholland }
1465 1.68 dholland AUTH_DESTROY(rpcua);
1466 1.68 dholland
1467 1.78 dholland DPRINTF("ping %x\n", dom->dom_server_addr.sin_addr.s_addr);
1468 1.68 dholland
1469 1.68 dholland if (sendto(pingsock, buf, outlen, 0,
1470 1.78 dholland (struct sockaddr *)(void *)&dom->dom_server_addr,
1471 1.78 dholland (socklen_t)(sizeof dom->dom_server_addr)) == -1)
1472 1.72 dholland yp_log(LOG_WARNING, "ping: sendto: %s", strerror(errno));
1473 1.68 dholland return 0;
1474 1.68 dholland
1475 1.68 dholland }
1476 1.68 dholland
1477 1.68 dholland /*
1478 1.95 dholland * Scan for timer-based work to do.
1479 1.95 dholland *
1480 1.95 dholland * If the domain is currently alive, ping the server we're currently
1481 1.95 dholland * bound to. Otherwise, try all known servers and/or broadcast for a
1482 1.95 dholland * server via nag_servers.
1483 1.68 dholland *
1484 1.95 dholland * Try again in five seconds.
1485 1.96 dholland *
1486 1.96 dholland * If we get back here and the state is still DOM_PINGING, it means
1487 1.96 dholland * we didn't receive a ping response within five seconds. Declare the
1488 1.96 dholland * binding lost. If the binding is already lost, and it's been lost
1489 1.96 dholland * for 60 seconds, switch to DOM_DEAD and begin exponential backoff.
1490 1.96 dholland * The exponential backoff starts at 10 seconds and tops out at one
1491 1.96 dholland * hour; see above.
1492 1.68 dholland */
1493 1.69 dholland static void
1494 1.68 dholland checkwork(void)
1495 1.68 dholland {
1496 1.78 dholland struct domain *dom;
1497 1.68 dholland time_t t;
1498 1.68 dholland
1499 1.68 dholland check = 0;
1500 1.68 dholland
1501 1.68 dholland (void)time(&t);
1502 1.78 dholland for (dom = domains; dom != NULL; dom = dom->dom_next) {
1503 1.96 dholland if (dom->dom_checktime >= t) {
1504 1.96 dholland continue;
1505 1.96 dholland }
1506 1.96 dholland switch (dom->dom_state) {
1507 1.96 dholland case DOM_NEW:
1508 1.96 dholland /* XXX should be a timeout for this state */
1509 1.96 dholland dom->dom_checktime = t + 5;
1510 1.96 dholland (void)nag_servers(dom);
1511 1.96 dholland break;
1512 1.96 dholland
1513 1.96 dholland case DOM_ALIVE:
1514 1.96 dholland dom->dom_state = DOM_PINGING;
1515 1.96 dholland dom->dom_checktime = t + 5;
1516 1.96 dholland (void)ping(dom);
1517 1.96 dholland break;
1518 1.96 dholland
1519 1.96 dholland case DOM_PINGING:
1520 1.96 dholland dom->dom_state = DOM_LOST;
1521 1.96 dholland dom->dom_losttime = t;
1522 1.78 dholland dom->dom_checktime = t + 5;
1523 1.98 dholland yp_log(LOG_NOTICE, "Domain %s lost its binding to "
1524 1.98 dholland "server %s", dom->dom_name,
1525 1.98 dholland inet_ntoa(dom->dom_server_addr.sin_addr));
1526 1.96 dholland (void)nag_servers(dom);
1527 1.96 dholland break;
1528 1.96 dholland
1529 1.96 dholland case DOM_LOST:
1530 1.96 dholland if (t > dom->dom_losttime + 60) {
1531 1.96 dholland dom->dom_state = DOM_DEAD;
1532 1.96 dholland dom->dom_backofftime = 10;
1533 1.98 dholland yp_log(LOG_NOTICE, "Domain %s dead; "
1534 1.98 dholland "going to exponential backoff",
1535 1.98 dholland dom->dom_name);
1536 1.96 dholland }
1537 1.96 dholland dom->dom_checktime = t + 5;
1538 1.96 dholland (void)nag_servers(dom);
1539 1.96 dholland break;
1540 1.96 dholland
1541 1.96 dholland case DOM_DEAD:
1542 1.96 dholland dom->dom_checktime = t + dom->dom_backofftime;
1543 1.96 dholland backoff(&dom->dom_backofftime);
1544 1.96 dholland (void)nag_servers(dom);
1545 1.96 dholland break;
1546 1.68 dholland }
1547 1.96 dholland /* re-fetch the time in case we hung sending packets */
1548 1.96 dholland (void)time(&t);
1549 1.68 dholland }
1550 1.68 dholland }
1551 1.68 dholland
1552 1.97 dholland /*
1553 1.97 dholland * Process a hangup signal.
1554 1.97 dholland *
1555 1.97 dholland * Do an extra nag_servers() for any domains that are DEAD. This way
1556 1.97 dholland * if you know things are back up you can restore service by sending
1557 1.97 dholland * ypbind a SIGHUP rather than waiting for the timeout period.
1558 1.97 dholland */
1559 1.97 dholland static void
1560 1.97 dholland dohup(void)
1561 1.97 dholland {
1562 1.97 dholland struct domain *dom;
1563 1.97 dholland
1564 1.97 dholland hupped = 0;
1565 1.97 dholland for (dom = domains; dom != NULL; dom = dom->dom_next) {
1566 1.97 dholland if (dom->dom_state == DOM_DEAD) {
1567 1.97 dholland (void)nag_servers(dom);
1568 1.97 dholland }
1569 1.97 dholland }
1570 1.97 dholland }
1571 1.97 dholland
1572 1.97 dholland /*
1573 1.97 dholland * Receive a hangup signal.
1574 1.97 dholland */
1575 1.97 dholland static void
1576 1.97 dholland hup(int __unused sig)
1577 1.97 dholland {
1578 1.97 dholland hupped = 1;
1579 1.97 dholland }
1580 1.97 dholland
1581 1.97 dholland /*
1582 1.97 dholland * Initialize hangup processing.
1583 1.97 dholland */
1584 1.97 dholland static void
1585 1.97 dholland starthup(void)
1586 1.97 dholland {
1587 1.97 dholland struct sigaction sa;
1588 1.97 dholland
1589 1.97 dholland sa.sa_handler = hup;
1590 1.97 dholland sigemptyset(&sa.sa_mask);
1591 1.97 dholland sa.sa_flags = SA_RESTART;
1592 1.97 dholland if (sigaction(SIGHUP, &sa, NULL) == -1) {
1593 1.97 dholland err(1, "sigaction");
1594 1.97 dholland }
1595 1.97 dholland }
1596 1.97 dholland
1597 1.68 dholland ////////////////////////////////////////////////////////////
1598 1.68 dholland // main
1599 1.68 dholland
1600 1.95 dholland /*
1601 1.95 dholland * Usage message.
1602 1.95 dholland */
1603 1.89 joerg __dead static void
1604 1.68 dholland usage(void)
1605 1.68 dholland {
1606 1.68 dholland const char *opt = "";
1607 1.68 dholland #ifdef DEBUG
1608 1.68 dholland opt = " [-d]";
1609 1.68 dholland #endif
1610 1.68 dholland
1611 1.68 dholland (void)fprintf(stderr,
1612 1.68 dholland "Usage: %s [-broadcast] [-insecure] [-ypset] [-ypsetme]%s\n",
1613 1.68 dholland getprogname(), opt);
1614 1.68 dholland exit(1);
1615 1.68 dholland }
1616 1.68 dholland
1617 1.95 dholland /*
1618 1.95 dholland * Main.
1619 1.95 dholland */
1620 1.68 dholland int
1621 1.68 dholland main(int argc, char *argv[])
1622 1.68 dholland {
1623 1.68 dholland struct timeval tv;
1624 1.68 dholland fd_set fdsr;
1625 1.68 dholland int width, lockfd;
1626 1.93 dholland int started = 0;
1627 1.91 dholland char *domainname;
1628 1.10 deraadt
1629 1.68 dholland setprogname(argv[0]);
1630 1.10 deraadt
1631 1.95 dholland /*
1632 1.95 dholland * Process arguments.
1633 1.95 dholland */
1634 1.95 dholland
1635 1.82 dholland default_ypbindmode = YPBIND_DIRECT;
1636 1.68 dholland while (--argc) {
1637 1.68 dholland ++argv;
1638 1.81 dholland if (!strcmp("-insecure", *argv)) {
1639 1.68 dholland insecure = 1;
1640 1.81 dholland } else if (!strcmp("-ypset", *argv)) {
1641 1.81 dholland allow_any_ypset = 1;
1642 1.81 dholland allow_local_ypset = 1;
1643 1.81 dholland } else if (!strcmp("-ypsetme", *argv)) {
1644 1.81 dholland allow_any_ypset = 0;
1645 1.81 dholland allow_local_ypset = 1;
1646 1.81 dholland } else if (!strcmp("-broadcast", *argv)) {
1647 1.82 dholland default_ypbindmode = YPBIND_BROADCAST;
1648 1.68 dholland #ifdef DEBUG
1649 1.81 dholland } else if (!strcmp("-d", *argv)) {
1650 1.81 dholland debug = 1;
1651 1.68 dholland #endif
1652 1.81 dholland } else {
1653 1.68 dholland usage();
1654 1.81 dholland }
1655 1.10 deraadt }
1656 1.10 deraadt
1657 1.95 dholland /*
1658 1.95 dholland * Look up the name of the default domain.
1659 1.95 dholland */
1660 1.95 dholland
1661 1.94 dholland (void)yp_get_default_domain(&domainname);
1662 1.94 dholland if (domainname[0] == '\0')
1663 1.94 dholland errx(1, "Domainname not set. Aborting.");
1664 1.94 dholland if (_yp_invalid_domain(domainname))
1665 1.94 dholland errx(1, "Invalid domainname: %s", domainname);
1666 1.94 dholland
1667 1.95 dholland /*
1668 1.95 dholland * Start things up.
1669 1.95 dholland */
1670 1.95 dholland
1671 1.95 dholland /* Open the system log. */
1672 1.68 dholland openlog("ypbind", LOG_PERROR | LOG_PID, LOG_DAEMON);
1673 1.10 deraadt
1674 1.95 dholland /* Acquire /var/run/ypbind.lock. */
1675 1.73 dholland lockfd = open_locked(_PATH_YPBIND_LOCK, O_CREAT|O_RDWR|O_TRUNC, 0644);
1676 1.68 dholland if (lockfd == -1)
1677 1.68 dholland err(1, "Cannot create %s", _PATH_YPBIND_LOCK);
1678 1.14 cgd
1679 1.97 dholland /* Accept hangups. */
1680 1.97 dholland starthup();
1681 1.97 dholland
1682 1.95 dholland /* Initialize sunrpc stuff. */
1683 1.74 dholland sunrpc_setup();
1684 1.1 deraadt
1685 1.95 dholland /* Clean out BINDINGDIR, deleting all existing (now stale) bindings */
1686 1.68 dholland if (purge_bindingdir(BINDINGDIR) < 0)
1687 1.95 dholland errx(1, "Unable to purge old bindings from %s", BINDINGDIR);
1688 1.95 dholland
1689 1.95 dholland /*
1690 1.95 dholland * We start with one binding, for the default domain. It starts
1691 1.95 dholland * out "unsuccessful".
1692 1.95 dholland *
1693 1.95 dholland * XXX: domain_create adds the new domain to 'domains' (the
1694 1.95 dholland * global linked list) and therefore we shouldn't assign
1695 1.95 dholland * 'domains' again on return.
1696 1.95 dholland */
1697 1.6 deraadt
1698 1.77 dholland domains = domain_create(domainname);
1699 1.95 dholland
1700 1.95 dholland /*
1701 1.95 dholland * Delete the lock for the default domain again, just in case something
1702 1.95 dholland * magically caused it to appear since purge_bindingdir() was called.
1703 1.95 dholland * XXX: this is useless and redundant; remove it.
1704 1.95 dholland */
1705 1.77 dholland removelock(domains);
1706 1.6 deraadt
1707 1.95 dholland /*
1708 1.95 dholland * Main loop. Wake up at least once a second and check for
1709 1.95 dholland * timer-based work to do (checkwork) and also handle incoming
1710 1.95 dholland * responses from ypservers and any RPCs made to the ypbind
1711 1.95 dholland * service.
1712 1.95 dholland *
1713 1.95 dholland * There are two sockets used for ypserver traffic: one for
1714 1.95 dholland * pings and one for everything else. These call XDR manually
1715 1.95 dholland * for encoding and are *not* dispatched via the sunrpc
1716 1.95 dholland * libraries.
1717 1.95 dholland *
1718 1.95 dholland * The ypbind serivce *is* dispatched via the sunrpc libraries.
1719 1.95 dholland * svc_getreqset() does whatever internal muck and ultimately
1720 1.95 dholland * ypbind service calls arrive at ypbindprog_2().
1721 1.95 dholland */
1722 1.68 dholland checkwork();
1723 1.68 dholland for (;;) {
1724 1.68 dholland width = svc_maxfd;
1725 1.68 dholland if (rpcsock > width)
1726 1.68 dholland width = rpcsock;
1727 1.68 dholland if (pingsock > width)
1728 1.68 dholland width = pingsock;
1729 1.68 dholland width++;
1730 1.68 dholland fdsr = svc_fdset;
1731 1.68 dholland FD_SET(rpcsock, &fdsr);
1732 1.68 dholland FD_SET(pingsock, &fdsr);
1733 1.68 dholland tv.tv_sec = 1;
1734 1.68 dholland tv.tv_usec = 0;
1735 1.20 cgd
1736 1.68 dholland switch (select(width, &fdsr, NULL, NULL, &tv)) {
1737 1.68 dholland case 0:
1738 1.95 dholland /* select timed out - check for timer-based work */
1739 1.97 dholland if (hupped) {
1740 1.97 dholland dohup();
1741 1.97 dholland }
1742 1.68 dholland checkwork();
1743 1.68 dholland break;
1744 1.68 dholland case -1:
1745 1.97 dholland if (hupped) {
1746 1.97 dholland dohup();
1747 1.97 dholland }
1748 1.97 dholland if (errno != EINTR) {
1749 1.97 dholland yp_log(LOG_WARNING, "select: %s",
1750 1.97 dholland strerror(errno));
1751 1.97 dholland }
1752 1.68 dholland break;
1753 1.68 dholland default:
1754 1.97 dholland if (hupped) {
1755 1.97 dholland dohup();
1756 1.97 dholland }
1757 1.95 dholland /* incoming of our own; read it */
1758 1.68 dholland if (FD_ISSET(rpcsock, &fdsr))
1759 1.68 dholland (void)handle_replies();
1760 1.68 dholland if (FD_ISSET(pingsock, &fdsr))
1761 1.68 dholland (void)handle_ping();
1762 1.95 dholland
1763 1.95 dholland /* read any incoming packets for the ypbind service */
1764 1.68 dholland svc_getreqset(&fdsr);
1765 1.95 dholland
1766 1.95 dholland /*
1767 1.95 dholland * Only check for timer-based work if
1768 1.95 dholland * something in the incoming RPC logic said
1769 1.95 dholland * to. This might be just a hack to avoid
1770 1.95 dholland * scanning the list unnecessarily, but I
1771 1.95 dholland * suspect it's also a hack to cover wrong
1772 1.95 dholland * state logic. - dholland 20140609
1773 1.95 dholland */
1774 1.68 dholland if (check)
1775 1.68 dholland checkwork();
1776 1.20 cgd break;
1777 1.68 dholland }
1778 1.20 cgd
1779 1.95 dholland /*
1780 1.95 dholland * Defer daemonizing until the default domain binds
1781 1.95 dholland * successfully. XXX: there seems to be no timeout
1782 1.95 dholland * on this, which means that if the default domain
1783 1.95 dholland * is dead upstream boot will hang indefinitely.
1784 1.95 dholland */
1785 1.96 dholland if (!started && domains->dom_state == DOM_ALIVE) {
1786 1.93 dholland started = 1;
1787 1.68 dholland #ifdef DEBUG
1788 1.68 dholland if (!debug)
1789 1.68 dholland #endif
1790 1.68 dholland (void)daemon(0, 0);
1791 1.68 dholland (void)pidfile(NULL);
1792 1.68 dholland }
1793 1.68 dholland }
1794 1.1 deraadt }
1795