Module Name:    src
Committed By:   dholland
Date:           Tue Jun 10 17:19:12 UTC 2014

Modified Files:
        src/usr.sbin/ypbind: ypbind.c

Log Message:
Load up with comments.


To generate a diff of this commit:
cvs rdiff -u -r1.94 -r1.95 src/usr.sbin/ypbind/ypbind.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/usr.sbin/ypbind/ypbind.c
diff -u src/usr.sbin/ypbind/ypbind.c:1.94 src/usr.sbin/ypbind/ypbind.c:1.95
--- src/usr.sbin/ypbind/ypbind.c:1.94	Tue Jun 10 17:19:00 2014
+++ src/usr.sbin/ypbind/ypbind.c	Tue Jun 10 17:19:12 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: ypbind.c,v 1.94 2014/06/10 17:19:00 dholland Exp $	*/
+/*	$NetBSD: ypbind.c,v 1.95 2014/06/10 17:19:12 dholland Exp $	*/
 
 /*
  * Copyright (c) 1992, 1993 Theo de Raadt <dera...@fsa.ca>
@@ -28,7 +28,7 @@
 
 #include <sys/cdefs.h>
 #ifndef LINT
-__RCSID("$NetBSD: ypbind.c,v 1.94 2014/06/10 17:19:00 dholland Exp $");
+__RCSID("$NetBSD: ypbind.c,v 1.95 2014/06/10 17:19:12 dholland Exp $");
 #endif
 
 #include <sys/types.h>
@@ -102,24 +102,33 @@ struct domain {
 
 #define BUFSIZE		1400
 
+/* the list of all domains */
 static struct domain *domains;
 static int check;
 
+/* option settings */
 static ypbind_mode_t default_ypbindmode;
-
 static int allow_local_ypset = 0, allow_any_ypset = 0;
 static int insecure;
 
+/* the sockets we use to interact with servers */
 static int rpcsock, pingsock;
+
+/* stuff used for manually interacting with servers */
 static struct rmtcallargs rmtca;
 static struct rmtcallres rmtcr;
 static bool_t rmtcr_outval;
 static unsigned long rmtcr_port;
+
+/* The ypbind service transports */
 static SVCXPRT *udptransp, *tcptransp;
 
 ////////////////////////////////////////////////////////////
 // utilities
 
+/*
+ * Combo of open() and flock().
+ */
 static int
 open_locked(const char *path, int flags, mode_t mode)
 {
@@ -148,6 +157,9 @@ static int debug;
 
 static void yp_log(int, const char *, ...) __printflike(2, 3);
 
+/*
+ * Log some stuff, to syslog or stderr depending on the debug setting.
+ */
 static void
 yp_log(int pri, const char *fmt, ...)
 {
@@ -185,6 +197,20 @@ ypservers_filename(const char *domain)
 ////////////////////////////////////////////////////////////
 // struct domain
 
+/*
+ * State transition is done like this: 
+ *
+ * STATE	EVENT		ACTION			NEWSTATE	TIMEOUT
+ * no binding	timeout		broadcast 		no binding	5 sec
+ * no binding	answer		--			binding		60 sec
+ * binding	timeout		ping server		checking	5 sec
+ * checking	timeout		ping server + broadcast	checking	5 sec
+ * checking	answer		--			binding		60 sec
+ */
+
+/*
+ * Look up a domain by the XID we assigned it.
+ */
 static struct domain *
 domain_find(uint32_t xid)
 {
@@ -196,6 +222,11 @@ domain_find(uint32_t xid)
 	return dom;
 }
 
+/*
+ * Pick an XID for a domain.
+ *
+ * XXX: this should just generate a random number.
+ */
 static uint32_t
 unique_xid(struct domain *dom)
 {
@@ -208,6 +239,10 @@ unique_xid(struct domain *dom)
 	return tmp_xid;
 }
 
+/*
+ * Construct a new domain. Adds it to the global linked list of all
+ * domains.
+ */
 static struct domain *
 domain_create(const char *name)
 {
@@ -263,6 +298,10 @@ domain_create(const char *name)
 ////////////////////////////////////////////////////////////
 // locks
 
+/*
+ * Open a new binding file. Does not write the contents out; the
+ * caller (there's only one) does that.
+ */
 static int
 makelock(struct domain *dom)
 {
@@ -284,6 +323,9 @@ makelock(struct domain *dom)
 	return fd;
 }
 
+/*
+ * Remove a binding file.
+ */
 static void
 removelock(struct domain *dom)
 {
@@ -295,12 +337,14 @@ removelock(struct domain *dom)
 }
 
 /*
- * purge_bindingdir: remove old binding files (i.e. "rm BINDINGDIR\/\*.[0-9]")
+ * purge_bindingdir: remove old binding files (i.e. "rm *.[0-9]" in BINDINGDIR)
+ *
+ * The local YP functions [e.g. yp_master()] will fail without even
+ * talking to ypbind if there is a stale (non-flock'd) binding file
+ * present.
  *
- * local YP functions [e.g. yp_master()] will fail without even talking
- * to ypbind if there is a stale (non-flock'd) binding file present.
- * we have to scan the entire BINDINGDIR for binding files, because
- * ypbind may bind more than just the yp_get_default_domain() domain.
+ * We have to remove all binding files in BINDINGDIR, not just the one
+ * for the default domain.
  */
 static int
 purge_bindingdir(const char *dirpath)
@@ -364,7 +408,17 @@ rpc_is_valid_response(char *name, struct
 }
 
 /*
- * LOOPBACK IS MORE IMPORTANT: PUT IN HACK
+ * Take note of the fact that we've received a reply from a ypserver.
+ * Or, in the case of being ypset, that we've been ypset, which
+ * functions much the same.
+ *
+ * Note that FORCE is set if and only if IS_YPSET is set.
+ *
+ * This function has also for the past 20+ years carried the annotation
+ *
+ *      LOOPBACK IS MORE IMPORTANT: PUT IN HACK
+ *
+ * whose meaning isn't entirely clear.
  */
 static void
 rpc_received(char *dom_name, struct sockaddr_in *raddrp, int force,
@@ -379,26 +433,50 @@ rpc_received(char *dom_name, struct sock
 	DPRINTF("returned from %s about %s\n",
 		inet_ntoa(raddrp->sin_addr), dom_name);
 
+	/* validate some stuff */
 	if (!rpc_is_valid_response(dom_name, raddrp)) {
 		return;
 	}
 
+	/* look for the domain */
 	for (dom = domains; dom != NULL; dom = dom->dom_next)
 		if (!strcmp(dom->dom_name, dom_name))
 			break;
 
+	/* if not found, create it, but only if FORCE; otherwise ignore */
 	if (dom == NULL) {
 		if (force == 0)
 			return;
 		dom = domain_create(dom_name);
 	}
 
+	/* the domain needs to know if it's been explicitly ypset */
 	if (is_ypset) {
 		dom->dom_been_ypset = 1;
 	}
 
-	/* soft update, alive */
+	/*
+	 * If the domain is alive and we aren't ypset, we don't need
+	 * to do anything.
+	 *
+	 * This code is unreachable (AFAIK) unless we receive an
+	 * unsolicited ping reply from the ypserver: because dom_alive
+	 * is 0 until we have a binding, but set from 1 to 2 when we
+	 * ping, it will never normally be 1 when a reply comes in,
+	 * even a reply to a ping. In the case where we lost the
+	 * binding and are getting a reply arising from nag_servers,
+	 * we lost the binding because we never got a ping response so
+	 * in that case dom_alive will also be 2.
+	 *
+	 * This logic is clearly wrong. XXX.
+	 */
 	if (dom->dom_alive == 1 && force == 0) {
+		/*
+		 * If the reply came from the server we expect, set
+		 * dom_alive back to 1 and ping again in 60 seconds.
+		 *
+		 * If it came from somewhere else, ignore it.
+		 */
 		if (!memcmp(&dom->dom_server_addr, raddrp,
 			    sizeof(dom->dom_server_addr))) {
 			dom->dom_alive = 1;
@@ -407,23 +485,63 @@ rpc_received(char *dom_name, struct sock
 		}
 		return;
 	}
-	
+
+	/*
+	 * Take the address we got the message from (or in the case of
+	 * ypset, the explicit address we were given) as the server
+	 * address for this domain, mark the domain alive, and we'll
+	 * check it again in 60 seconds.
+	 *
+	 * XXX: it looks like if we get a random unsolicited reply
+	 * from somewhere, we'll silently switch to that server
+	 * address, regardless of merit.
+	 *
+	 * 1. If we have a foo.ypservers file the address should be
+	 * checked against it and rejected if it's not one of the
+	 * addresses of one of the listed hostnames. Note that it
+	 * might not be the same address we sent to; even fairly smart
+	 * UDP daemons don't always handle multihomed hosts correctly
+	 * and we can't expect sunrpc code to do anything intelligent
+	 * at all.
+	 *
+	 * 2. If we're in broadcast mode the address should be
+	 * checked against the local addresses and netmasks so we
+	 * don't accept responses from Mars.
+	 *
+	 * 2a. If we're in broadcast mode and we've been ypset, we
+	 * should not accept anything else until we drop the ypset
+	 * state for not responding.
+	 *
+	 * 3. Either way we should not accept a response from an
+	 * arbitrary host unless we don't currently have a binding.
+	 * (The logic in the previous if statement is probably
+	 * supposed to handle this, but it doesn't currently work.)
+	 *
+	 * Note that for a random unsolicited reply to work it has to
+	 * carry the XID of one of the domains we know about; but
+	 * those values are predictable.
+	 */
 	(void)memcpy(&dom->dom_server_addr, raddrp,
 	    sizeof(dom->dom_server_addr));
 	/* recheck binding in 60 seconds */
 	dom->dom_checktime = time(NULL) + 60;
 	dom->dom_alive = 1;
 
+	/*
+	 * Generate a new binding file. If this fails, forget about it.
+	 * (But we keep the binding and we'll report it to anyone who
+	 * asks via the ypbind service.) XXX: this will interact badly,
+	 * maybe very badly, with the code in HEURISTIC.
+	 *
+	 * Note that makelock() doesn't log on failure.
+	 */
+
 	if (dom->dom_lockfd != -1)
 		(void)close(dom->dom_lockfd);
 
 	if ((fd = makelock(dom)) == -1)
 		return;
 
-	/*
-	 * ok, if BINDINGDIR exists, and we can create the binding file,
-	 * then write to it..
-	 */
 	dom->dom_lockfd = fd;
 
 	iov[0].iov_base = &(udptransp->xp_port);
@@ -450,6 +568,10 @@ rpc_received(char *dom_name, struct sock
 	}
 }
 
+/*
+ * The NULL call: do nothing. This is obliged to exist because of
+ * sunrpc silliness.
+ */
 static void *
 /*ARGSUSED*/
 ypbindproc_null_2(SVCXPRT *transp, void *argp)
@@ -461,6 +583,9 @@ ypbindproc_null_2(SVCXPRT *transp, void 
 	return (void *)&res;
 }
 
+/*
+ * The DOMAIN call: look up the ypserver for a specified domain.
+ */
 static void *
 /*ARGSUSED*/
 ypbindproc_domain_2(SVCXPRT *transp, void *argp)
@@ -472,12 +597,23 @@ ypbindproc_domain_2(SVCXPRT *transp, voi
 	int count;
 
 	DPRINTF("ypbindproc_domain_2 %s\n", arg);
+
+	/* Reject invalid domains. */
 	if (_yp_invalid_domain(arg))
 		return NULL;
 
 	(void)memset(&res, 0, sizeof res);
 	res.ypbind_status = YPBIND_FAIL_VAL;
 
+	/*
+	 * Look for the domain. XXX: Behave erratically if we have
+	 * more than 100 domains. The intent here is to avoid allowing
+	 * arbitrary incoming requests to create more than 100
+	 * domains; but this logic means that if we legitimately have
+	 * more than 100 (e.g. via ypset) we'll only actually bind the
+	 * first 100 and the rest will fail. The test on 'count' should
+	 * be moved further down.
+	 */
 	for (count = 0, dom = domains;
 	    dom != NULL;
 	    dom = dom->dom_next, count++) {
@@ -487,6 +623,16 @@ ypbindproc_domain_2(SVCXPRT *transp, voi
 			break;
 	}
 
+	/*
+	 * If the domain doesn't exist, create it, then fail the call
+	 * because we have no information yet.
+	 *
+	 * Set "check" so that checkwork() will run and look for a
+	 * server.
+	 *
+	 * XXX: like during startup there's a spurious call to
+	 * removelock() after domain_create().
+	 */
 	if (dom == NULL) {
 		dom = domain_create(arg);
 		removelock(dom);
@@ -501,6 +647,15 @@ ypbindproc_domain_2(SVCXPRT *transp, voi
 	}
 
 #ifdef HEURISTIC
+	/*
+	 * Keep track of the last time we were explicitly asked about
+	 * this domain. If it happens a lot, force a ping. This works
+	 * (or "works") because we only get asked specifically when
+	 * things aren't going; otherwise the client code in libc and
+	 * elsewhere uses the binding file.
+	 *
+	 * Note: HEURISTIC is enabled by default.
+	 */
 	(void)time(&now);
 	if (now < dom->dom_asktime + 5) {
 		/*
@@ -528,6 +683,19 @@ ypbindproc_domain_2(SVCXPRT *transp, voi
 	return &res;
 }
 
+/*
+ * The SETDOM call: ypset.
+ *
+ * Unless -ypsetme was given on the command line, this is rejected;
+ * even then it's only allowed from localhost unless -ypset was
+ * given on the command line.
+ *
+ * Allowing anyone anywhere to ypset you (and therefore provide your
+ * password file and such) is a horrible thing and it isn't clear to
+ * me why this functionality even exists.
+ *
+ * ypset from localhost has some but limited utility.
+ */
 static void *
 ypbindproc_setdom_2(SVCXPRT *transp, void *argp)
 {
@@ -539,6 +707,10 @@ ypbindproc_setdom_2(SVCXPRT *transp, voi
 	fromsin = svc_getcaller(transp);
 	DPRINTF("ypbindproc_setdom_2 from %s\n", inet_ntoa(fromsin->sin_addr));
 
+	/*
+	 * Reject unless enabled.
+	 */
+
 	if (allow_any_ypset) {
 		/* nothing */
 	} else if (allow_local_ypset) {
@@ -552,16 +724,27 @@ ypbindproc_setdom_2(SVCXPRT *transp, voi
 		return NULL;
 	}
 
+	/* Make a "security" check. */
 	if (ntohs(fromsin->sin_port) >= IPPORT_RESERVED) {
 		DPRINTF("ypset from unprivileged port denied\n");
 		return &res;
 	}
 
+	/* Ignore requests we don't understand. */
 	if (sd->ypsetdom_vers != YPVERS) {
 		DPRINTF("ypset with wrong version denied\n");
 		return &res;
 	}
 
+	/*
+	 * Fetch the arguments out of the xdr-decoded blob and call
+	 * rpc_received(), setting FORCE so that the domain will be
+	 * created if we don't already know about it, and also saying
+	 * that it's actually a ypset.
+	 *
+	 * Effectively we're telilng rpc_received() that we got an
+	 * RPC response from the server specified by ypset.
+	 */
 	(void)memset(&bindsin, 0, sizeof bindsin);
 	bindsin.sin_family = AF_INET;
 	bindsin.sin_len = sizeof(bindsin);
@@ -575,6 +758,13 @@ ypbindproc_setdom_2(SVCXPRT *transp, voi
 	return &res;
 }
 
+/*
+ * Dispatcher for the ypbind service.
+ *
+ * There are three calls: NULL, which does nothing, DOMAIN, which
+ * gets the binding for a particular domain, and SETDOM, which
+ * does ypset.
+ */
 static void
 ypbindprog_2(struct svc_req *rqstp, register SVCXPRT *transp)
 {
@@ -636,6 +826,12 @@ ypbindprog_2(struct svc_req *rqstp, regi
 	return;
 }
 
+/*
+ * Set up sunrpc stuff.
+ *
+ * This sets up the ypbind service (both TCP and UDP) and also opens
+ * the sockets we use for talking to ypservers.
+ */
 static void
 sunrpc_setup(void)
 {
@@ -684,6 +880,10 @@ sunrpc_setup(void)
 ////////////////////////////////////////////////////////////
 // operational logic
 
+/*
+ * Broadcast an RPC packet to hopefully contact some servers for a
+ * domain.
+ */
 static int
 broadcast(char *buf, int outlen)
 {
@@ -734,6 +934,13 @@ broadcast(char *buf, int outlen)
 	return (0);
 }
 
+/*
+ * Send an RPC packet to all the configured (in /var/yp/foo.ypservers)
+ * servers for a domain.
+ *
+ * XXX: we should read and parse the file up front and reread it only
+ * if it changes.
+ */
 static int
 direct(char *buf, int outlen, struct domain *dom)
 {
@@ -815,6 +1022,11 @@ direct(char *buf, int outlen, struct dom
 	return 0;
 }
 
+/*
+ * Send an RPC packet to the server that's been selected with ypset.
+ * (This is only used when in broadcast mode and when ypset is
+ * allowed.)
+ */
 static int
 direct_set(char *buf, int outlen, struct domain *dom)
 {
@@ -871,6 +1083,9 @@ direct_set(char *buf, int outlen, struct
 	return 0;
 }
 
+/*
+ * Receive and dispatch packets on the general RPC socket.
+ */
 static enum clnt_stat
 handle_replies(void)
 {
@@ -925,6 +1140,9 @@ try_again:
 	return RPC_SUCCESS;
 }
 
+/*
+ * Receive and dispatch packets on the ping socket.
+ */
 static enum clnt_stat
 handle_ping(void)
 {
@@ -979,6 +1197,14 @@ try_again:
 	return RPC_SUCCESS;
 }
 
+/*
+ * Contact all known servers for a domain in the hopes that one of
+ * them's awake. Also, if we previously had a binding but it timed
+ * out, try the portmapper on that host in case ypserv moved ports for
+ * some reason.
+ *
+ * As a side effect, wipe out any existing binding file.
+ */
 static int
 nag_servers(struct domain *dom)
 {
@@ -1072,6 +1298,11 @@ nag_servers(struct domain *dom)
 	return -1;
 }
 
+/*
+ * Send a ping message to a domain's current ypserver.
+ *
+ * Note that dom_alive is 2 while waiting for the response.
+ */
 static int
 ping(struct domain *dom)
 {
@@ -1133,14 +1364,13 @@ ping(struct domain *dom)
 }
 
 /*
- * State transition is done like this: 
+ * Scan for timer-based work to do.
  *
- * STATE	EVENT		ACTION			NEWSTATE	TIMEOUT
- * no binding	timeout		broadcast 		no binding	5 sec
- * no binding	answer		--			binding		60 sec
- * binding	timeout		ping server		checking	5 sec
- * checking	timeout		ping server + broadcast	checking	5 sec
- * checking	answer		--			binding		60 sec
+ * If the domain is currently alive, ping the server we're currently
+ * bound to. Otherwise, try all known servers and/or broadcast for a
+ * server via nag_servers.
+ *
+ * Try again in five seconds.
  */
 static void
 checkwork(void)
@@ -1166,6 +1396,9 @@ checkwork(void)
 ////////////////////////////////////////////////////////////
 // main
 
+/*
+ * Usage message.
+ */
 __dead static void
 usage(void)
 {
@@ -1180,6 +1413,9 @@ usage(void)
 	exit(1);
 }
 
+/*
+ * Main.
+ */
 int
 main(int argc, char *argv[])
 {
@@ -1191,6 +1427,10 @@ main(int argc, char *argv[])
 
 	setprogname(argv[0]);
 
+	/*
+	 * Process arguments.
+	 */
+
 	default_ypbindmode = YPBIND_DIRECT;
 	while (--argc) {
 		++argv;
@@ -1213,33 +1453,69 @@ main(int argc, char *argv[])
 		}
 	}
 
+	/*
+	 * Look up the name of the default domain.
+	 */
+
 	(void)yp_get_default_domain(&domainname);
 	if (domainname[0] == '\0')
 		errx(1, "Domainname not set. Aborting.");
 	if (_yp_invalid_domain(domainname))
 		errx(1, "Invalid domainname: %s", domainname);
 
-	/* initialise syslog */
+	/*
+	 * Start things up.
+	 */
+
+	/* Open the system log. */
 	openlog("ypbind", LOG_PERROR | LOG_PID, LOG_DAEMON);
 
-	/* acquire ypbind.lock */
+	/* Acquire /var/run/ypbind.lock. */
 	lockfd = open_locked(_PATH_YPBIND_LOCK, O_CREAT|O_RDWR|O_TRUNC, 0644);
 	if (lockfd == -1)
 		err(1, "Cannot create %s", _PATH_YPBIND_LOCK);
 
-	/* initialize sunrpc stuff */
+	/* Initialize sunrpc stuff. */
 	sunrpc_setup();
 
-	/* blow away old bindings in BINDINGDIR */
+	/* Clean out BINDINGDIR, deleting all existing (now stale) bindings */
 	if (purge_bindingdir(BINDINGDIR) < 0)
-		errx(1, "unable to purge old bindings from %s", BINDINGDIR);
+		errx(1, "Unable to purge old bindings from %s", BINDINGDIR);
+
+	/*
+	 * We start with one binding, for the default domain. It starts
+	 * out "unsuccessful".
+	 *
+	 * XXX: domain_create adds the new domain to 'domains' (the
+	 * global linked list) and therefore we shouldn't assign
+	 * 'domains' again on return.
+	 */
 
-	/* build initial domain binding, make it "unsuccessful" */
 	domains = domain_create(domainname);
+
+	/*
+	 * Delete the lock for the default domain again, just in case something
+	 * magically caused it to appear since purge_bindingdir() was called.
+	 * XXX: this is useless and redundant; remove it.
+	 */
 	removelock(domains);
 
+	/*
+	 * Main loop. Wake up at least once a second and check for
+	 * timer-based work to do (checkwork) and also handle incoming
+	 * responses from ypservers and any RPCs made to the ypbind
+	 * service.
+	 *
+	 * There are two sockets used for ypserver traffic: one for
+	 * pings and one for everything else. These call XDR manually
+	 * for encoding and are *not* dispatched via the sunrpc
+	 * libraries.
+	 *
+	 * The ypbind serivce *is* dispatched via the sunrpc libraries.
+	 * svc_getreqset() does whatever internal muck and ultimately
+	 * ypbind service calls arrive at ypbindprog_2().
+	 */
 	checkwork();
-
 	for (;;) {
 		width = svc_maxfd;
 		if (rpcsock > width)
@@ -1255,22 +1531,41 @@ main(int argc, char *argv[])
 
 		switch (select(width, &fdsr, NULL, NULL, &tv)) {
 		case 0:
+			/* select timed out - check for timer-based work */
 			checkwork();
 			break;
 		case -1:
 			yp_log(LOG_WARNING, "select: %s", strerror(errno));
 			break;
 		default:
+			/* incoming of our own; read it */
 			if (FD_ISSET(rpcsock, &fdsr))
 				(void)handle_replies();
 			if (FD_ISSET(pingsock, &fdsr))
 				(void)handle_ping();
+
+			/* read any incoming packets for the ypbind service */
 			svc_getreqset(&fdsr);
+
+			/*
+			 * Only check for timer-based work if
+			 * something in the incoming RPC logic said
+			 * to. This might be just a hack to avoid
+			 * scanning the list unnecessarily, but I
+			 * suspect it's also a hack to cover wrong
+			 * state logic. - dholland 20140609
+			 */
 			if (check)
 				checkwork();
 			break;
 		}
 
+		/*
+		 * Defer daemonizing until the default domain binds
+		 * successfully. XXX: there seems to be no timeout
+		 * on this, which means that if the default domain
+		 * is dead upstream boot will hang indefinitely.
+		 */
 		if (!started && domains->dom_alive) {
 			started = 1;
 #ifdef DEBUG
@@ -1281,3 +1576,4 @@ main(int argc, char *argv[])
 		}
 	}
 }
+

Reply via email to