xref: /freebsd/usr.sbin/ypserv/yp_main.c (revision 8ddb146abcdf061be9f2c0db7e391697dafad85c)
1 /*-
2  * SPDX-License-Identifier: BSD-4-Clause
3  *
4  * Copyright (c) 1995
5  *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by Bill Paul.
18  * 4. Neither the name of the author nor the names of any co-contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37 
38 /*
39  * ypserv startup function.
40  * We need out own main() since we have to do some additional work
41  * that rpcgen won't do for us. Most of this file was generated using
42  * rpcgen.new, and later modified.
43  */
44 
45 #include <sys/types.h>
46 #include <sys/mman.h>
47 #include <sys/queue.h>
48 #include <sys/socket.h>
49 #include <sys/wait.h>
50 #include "yp.h"
51 #include <err.h>
52 #include <errno.h>
53 #include <memory.h>
54 #include <stdio.h>
55 #include <signal.h>
56 #include <stdarg.h>
57 #include <stdlib.h> /* getenv, exit */
58 #include <string.h> /* strcmp */
59 #include <syslog.h>
60 #include <unistd.h>
61 #ifdef __cplusplus
62 #include <sysent.h> /* getdtablesize, open */
63 #endif /* __cplusplus */
64 #include <netinet/in.h>
65 #include <netdb.h>
66 #include "yp_extern.h"
67 #include <netconfig.h>
68 #include <rpc/rpc.h>
69 #include <rpc/rpc_com.h>
70 
71 #ifndef SIG_PF
72 #define	SIG_PF void(*)(int)
73 #endif
74 
75 #define	_RPCSVC_CLOSEDOWN 120
76 int _rpcpmstart;		/* Started by a port monitor ? */
77 static int _rpcfdtype;  /* Whether Stream or Datagram? */
78 static int _rpcaf;
79 static int _rpcfd;
80 
81 /* States a server can be in wrt request */
82 #define	_IDLE 0
83 #define	_SERVED 1
84 #define	_SERVING 2
85 
86 extern void ypprog_1(struct svc_req *, SVCXPRT *);
87 extern void ypprog_2(struct svc_req *, SVCXPRT *);
88 extern int _rpc_dtablesize(void);
89 extern int _rpcsvcstate;	 /* Set when a request is serviced */
90 char *progname = "ypserv";
91 char *yp_dir = _PATH_YP;
92 int debug;
93 int do_dns = 0;
94 int resfd;
95 
96 struct socklistent {
97 	int				sle_sock;
98 	struct sockaddr_storage		sle_ss;
99 	SLIST_ENTRY(socklistent)	sle_next;
100 };
101 static SLIST_HEAD(, socklistent) sle_head =
102 	SLIST_HEAD_INITIALIZER(sle_head);
103 
104 struct bindaddrlistent {
105 	const char			*ble_hostname;
106 	SLIST_ENTRY(bindaddrlistent)	ble_next;
107 };
108 static SLIST_HEAD(, bindaddrlistent) ble_head =
109 	SLIST_HEAD_INITIALIZER(ble_head);
110 
111 static char *servname = "0";
112 
113 static
114 void _msgout(char* msg, ...)
115 {
116 	va_list ap;
117 
118 	va_start(ap, msg);
119 	if (debug) {
120 		if (_rpcpmstart)
121 			vsyslog(LOG_ERR, msg, ap);
122 		else
123 			vwarnx(msg, ap);
124 	} else
125 		vsyslog(LOG_ERR, msg, ap);
126 	va_end(ap);
127 }
128 
129 pid_t	yp_pid;
130 
131 static void
132 yp_svc_run(void)
133 {
134 #ifdef FD_SETSIZE
135 	fd_set readfds;
136 #else
137 	int readfds;
138 #endif /* def FD_SETSIZE */
139 	int fd_setsize = _rpc_dtablesize();
140 	struct timeval timeout;
141 
142 	/* Establish the identity of the parent ypserv process. */
143 	yp_pid = getpid();
144 
145 	for (;;) {
146 #ifdef FD_SETSIZE
147 		readfds = svc_fdset;
148 #else
149 		readfds = svc_fds;
150 #endif /* def FD_SETSIZE */
151 
152 		FD_SET(resfd, &readfds);
153 
154 		timeout.tv_sec = RESOLVER_TIMEOUT;
155 		timeout.tv_usec = 0;
156 		switch (select(fd_setsize, &readfds, NULL, NULL,
157 			       &timeout)) {
158 		case -1:
159 			if (errno == EINTR) {
160 				continue;
161 			}
162 			warn("svc_run: - select failed");
163 			return;
164 		case 0:
165 			if (getpid() == yp_pid)
166 				yp_prune_dnsq();
167 			break;
168 		default:
169 			if (getpid() == yp_pid) {
170 				if (FD_ISSET(resfd, &readfds)) {
171 					yp_run_dnsq();
172 					FD_CLR(resfd, &readfds);
173 				}
174 				svc_getreqset(&readfds);
175 			}
176 		}
177 		if (yp_pid != getpid())
178 			_exit(0);
179 	}
180 }
181 
182 static void
183 unregister(void)
184 {
185 	(void)svc_unreg(YPPROG, YPVERS);
186 	(void)svc_unreg(YPPROG, YPOLDVERS);
187 }
188 
189 static void
190 reaper(int sig)
191 {
192 	int			status;
193 	int			saved_errno;
194 
195 	saved_errno = errno;
196 
197 	if (sig == SIGHUP) {
198 		load_securenets();
199 #ifdef DB_CACHE
200 		yp_flush_all();
201 #endif
202 		errno = saved_errno;
203 		return;
204 	}
205 
206 	if (sig == SIGCHLD) {
207 		while (wait3(&status, WNOHANG, NULL) > 0)
208 			children--;
209 	} else {
210 		unregister();
211 		exit(0);
212 	}
213 	errno = saved_errno;
214 	return;
215 }
216 
217 static void
218 usage(void)
219 {
220 	fprintf(stderr, "usage: ypserv [-h addr] [-d] [-n] [-p path] [-P port]\n");
221 	exit(1);
222 }
223 
224 static void
225 closedown(int sig)
226 {
227 	if (_rpcsvcstate == _IDLE) {
228 		extern fd_set svc_fdset;
229 		static int size;
230 		int i, openfd;
231 
232 		if (_rpcfdtype == SOCK_DGRAM) {
233 			unregister();
234 			exit(0);
235 		}
236 		if (size == 0) {
237 			size = getdtablesize();
238 		}
239 		for (i = 0, openfd = 0; i < size && openfd < 2; i++)
240 			if (FD_ISSET(i, &svc_fdset))
241 				openfd++;
242 		if (openfd <= 1) {
243 			unregister();
244 			exit(0);
245 		}
246 	}
247 	if (_rpcsvcstate == _SERVED)
248 		_rpcsvcstate = _IDLE;
249 
250 	(void) signal(SIGALRM, (SIG_PF) closedown);
251 	(void) alarm(_RPCSVC_CLOSEDOWN/2);
252 }
253 
254 static int
255 create_service(const int sock, const struct netconfig *nconf,
256 	const struct __rpc_sockinfo *si)
257 {
258 	int error;
259 
260 	SVCXPRT *transp;
261 	struct addrinfo hints, *res, *res0;
262 	struct socklistent *slep;
263 	struct bindaddrlistent *blep;
264 	struct netbuf svcaddr;
265 
266 	SLIST_INIT(&sle_head);
267 	memset(&hints, 0, sizeof(hints));
268 	memset(&svcaddr, 0, sizeof(svcaddr));
269 
270 	hints.ai_family = si->si_af;
271 	hints.ai_socktype = si->si_socktype;
272 	hints.ai_protocol = si->si_proto;
273 
274 	/*
275 	 * Build socketlist from bindaddrlist.
276 	 */
277 	if (sock == RPC_ANYFD) {
278 		SLIST_FOREACH(blep, &ble_head, ble_next) {
279 			if (blep->ble_hostname == NULL)
280 				hints.ai_flags = AI_PASSIVE;
281 			else
282 				hints.ai_flags = 0;
283 			error = getaddrinfo(blep->ble_hostname, servname,
284 				    &hints, &res0);
285 			if (error) {
286 				_msgout("getaddrinfo(): %s",
287 				    gai_strerror(error));
288 				return -1;
289 			}
290 			for (res = res0; res; res = res->ai_next) {
291 				int s;
292 
293 				s = __rpc_nconf2fd(nconf);
294 				if (s < 0) {
295 					if (errno == EAFNOSUPPORT)
296 						_msgout("unsupported"
297 						    " transport: %s",
298 						    nconf->nc_netid);
299 					else
300 						_msgout("cannot create"
301 						    " %s socket: %s",
302 						    nconf->nc_netid,
303 						    strerror(errno));
304 					freeaddrinfo(res0);
305 					return -1;
306 				}
307 				if (bindresvport_sa(s, res->ai_addr) == -1) {
308 					if ((errno != EPERM) ||
309 					    (bind(s, res->ai_addr,
310 					    res->ai_addrlen) == -1)) {
311 						_msgout("cannot bind "
312 						    "%s socket: %s",
313 						    nconf->nc_netid,
314 						strerror(errno));
315 						freeaddrinfo(res0);
316 						close(sock);
317 						return -1;
318 					}
319 				}
320 				if (nconf->nc_semantics != NC_TPI_CLTS)
321 					listen(s, SOMAXCONN);
322 
323 				slep = malloc(sizeof(*slep));
324 				if (slep == NULL) {
325 					_msgout("malloc failed: %s",
326 					    strerror(errno));
327 					freeaddrinfo(res0);
328 					close(s);
329 					return -1;
330 				}
331 				memset(slep, 0, sizeof(*slep));
332 				memcpy(&slep->sle_ss, res->ai_addr,
333 				    res->ai_addrlen);
334 				slep->sle_sock = s;
335 				SLIST_INSERT_HEAD(&sle_head, slep, sle_next);
336 
337 				/*
338 				 * If servname == "0", redefine it by using
339 				 * the bound socket.
340 				 */
341 				if (strncmp("0", servname, 1) == 0) {
342 					struct sockaddr *sap;
343 					socklen_t slen;
344 					char *sname;
345 
346 					sname = malloc(NI_MAXSERV);
347 					if (sname == NULL) {
348 						_msgout("malloc(): %s",
349 						    strerror(errno));
350 						freeaddrinfo(res0);
351 						close(s);
352 						return -1;
353 					}
354 					memset(sname, 0, NI_MAXSERV);
355 
356 					sap = (struct sockaddr *)&slep->sle_ss;
357 					slen = sizeof(*sap);
358 					error = getsockname(s, sap, &slen);
359 					if (error) {
360 						_msgout("getsockname(): %s",
361 						    strerror(errno));
362 						freeaddrinfo(res0);
363 						close(s);
364 						free(sname);
365 						return -1;
366 					}
367 					error = getnameinfo(sap, slen,
368 					    NULL, 0,
369 					    sname, NI_MAXSERV,
370 					    NI_NUMERICHOST | NI_NUMERICSERV);
371 					if (error) {
372 						_msgout("getnameinfo(): %s",
373 						    strerror(errno));
374 						freeaddrinfo(res0);
375 						close(s);
376 						free(sname);
377 						return -1;
378 					}
379 					servname = sname;
380 				}
381 			}
382 			freeaddrinfo(res0);
383 		}
384 	} else {
385 		slep = malloc(sizeof(*slep));
386 		if (slep == NULL) {
387 			_msgout("malloc failed: %s", strerror(errno));
388 			return -1;
389 		}
390 		memset(slep, 0, sizeof(*slep));
391 		slep->sle_sock = sock;
392 		SLIST_INSERT_HEAD(&sle_head, slep, sle_next);
393 	}
394 
395 	/*
396 	 * Traverse socketlist and create rpc service handles for each socket.
397 	 */
398 	SLIST_FOREACH(slep, &sle_head, sle_next) {
399 		if (nconf->nc_semantics == NC_TPI_CLTS)
400 			transp = svc_dg_create(slep->sle_sock, 0, 0);
401 		else
402 			transp = svc_vc_create(slep->sle_sock, RPC_MAXDATASIZE,
403 			    RPC_MAXDATASIZE);
404 		if (transp == NULL) {
405 			_msgout("unable to create service: %s",
406 			    nconf->nc_netid);
407 			continue;
408 		}
409 		if (!svc_reg(transp, YPPROG, YPOLDVERS, ypprog_1, NULL)) {
410 			svc_destroy(transp);
411 			close(slep->sle_sock);
412 			_msgout("unable to register (YPPROG, YPOLDVERS, %s):"
413 			    " %s", nconf->nc_netid, strerror(errno));
414 			continue;
415 		}
416 		if (!svc_reg(transp, YPPROG, YPVERS, ypprog_2, NULL)) {
417 			svc_destroy(transp);
418 			close(slep->sle_sock);
419 			_msgout("unable to register (YPPROG, YPVERS, %s): %s",
420 			    nconf->nc_netid, strerror(errno));
421 			continue;
422 		}
423 	}
424 	while(!(SLIST_EMPTY(&sle_head)))
425 		SLIST_REMOVE_HEAD(&sle_head, sle_next);
426 
427 	/*
428 	 * Register RPC service to rpcbind by using AI_PASSIVE address.
429 	 */
430 	hints.ai_flags = AI_PASSIVE;
431 	error = getaddrinfo(NULL, servname, &hints, &res0);
432 	if (error) {
433 		_msgout("getaddrinfo(): %s", gai_strerror(error));
434 		return -1;
435 	}
436 	svcaddr.buf = res0->ai_addr;
437 	svcaddr.len = res0->ai_addrlen;
438 
439 	if (si->si_af == AF_INET) {
440 		/* XXX: ignore error intentionally */
441 		rpcb_set(YPPROG, YPOLDVERS, nconf, &svcaddr);
442 	}
443 	/* XXX: ignore error intentionally */
444 	rpcb_set(YPPROG, YPVERS, nconf, &svcaddr);
445 	freeaddrinfo(res0);
446 	return 0;
447 }
448 
449 int
450 main(int argc, char *argv[])
451 {
452 	int ch;
453 	int error;
454 	int ntrans;
455 
456 	void *nc_handle;
457 	struct netconfig *nconf;
458 	struct __rpc_sockinfo si;
459 	struct bindaddrlistent *blep;
460 
461 	memset(&si, 0, sizeof(si));
462 	SLIST_INIT(&ble_head);
463 
464 	while ((ch = getopt(argc, argv, "dh:np:P:")) != -1) {
465 		switch (ch) {
466 		case 'd':
467 			debug = ypdb_debug = 1;
468 			break;
469 		case 'h':
470 			blep = malloc(sizeof(*blep));
471 			if (blep == NULL)
472 				err(1, "malloc() failed: -h %s", optarg);
473 			blep->ble_hostname = optarg;
474 			SLIST_INSERT_HEAD(&ble_head, blep, ble_next);
475 			break;
476 		case 'n':
477 			do_dns = 1;
478 			break;
479 		case 'p':
480 			yp_dir = optarg;
481 			break;
482 		case 'P':
483 			servname = optarg;
484 			break;
485 		default:
486 			usage();
487 		}
488 	}
489 	/*
490 	 * Add "anyaddr" entry if no -h is specified.
491 	 */
492 	if (SLIST_EMPTY(&ble_head)) {
493 		blep = malloc(sizeof(*blep));
494 		if (blep == NULL)
495 			err(1, "malloc() failed");
496 		memset(blep, 0, sizeof(*blep));
497 		SLIST_INSERT_HEAD(&ble_head, blep, ble_next);
498 	}
499 
500 	load_securenets();
501 	yp_init_resolver();
502 #ifdef DB_CACHE
503 	yp_init_dbs();
504 #endif
505 	nc_handle = setnetconfig();
506 	if (nc_handle == NULL)
507 		err(1, "cannot read %s", NETCONFIG);
508 	if (__rpc_fd2sockinfo(0, &si) != 0) {
509 		/* invoked from inetd */
510 		_rpcpmstart = 1;
511 		_rpcfdtype = si.si_socktype;
512 		_rpcaf = si.si_af;
513 		_rpcfd = 0;
514 		openlog("ypserv", LOG_PID, LOG_DAEMON);
515 	} else {
516 		/* standalone mode */
517 		if (!debug) {
518 			if (daemon(0,0)) {
519 				err(1,"cannot fork");
520 			}
521 			openlog("ypserv", LOG_PID, LOG_DAEMON);
522 		}
523 		_rpcpmstart = 0;
524 		_rpcaf = AF_INET;
525 		_rpcfd = RPC_ANYFD;
526 		unregister();
527 	}
528 
529 	if (madvise(NULL, 0, MADV_PROTECT) != 0)
530 		_msgout("madvise(): %s", strerror(errno));
531 
532 	/*
533 	 * Create RPC service for each transport.
534 	 */
535 	ntrans = 0;
536 	while((nconf = getnetconfig(nc_handle))) {
537 		if ((nconf->nc_flag & NC_VISIBLE)) {
538 			if (__rpc_nconf2sockinfo(nconf, &si) == 0) {
539 				_msgout("cannot get information for %s.  "
540 				    "Ignored.", nconf->nc_netid);
541 				continue;
542 			}
543 			if (_rpcpmstart) {
544 				if (si.si_socktype != _rpcfdtype ||
545 				    si.si_af != _rpcaf)
546 					continue;
547 			} else if (si.si_af != _rpcaf)
548 					continue;
549 			error = create_service(_rpcfd, nconf, &si);
550 			if (error) {
551 				endnetconfig(nc_handle);
552 				exit(1);
553 			}
554 			ntrans++;
555 		}
556 	}
557 	endnetconfig(nc_handle);
558 	while(!(SLIST_EMPTY(&ble_head)))
559 		SLIST_REMOVE_HEAD(&ble_head, ble_next);
560 	if (ntrans == 0) {
561 		_msgout("no transport is available.  Aborted.");
562 		exit(1);
563 	}
564 	if (_rpcpmstart) {
565 		(void) signal(SIGALRM, (SIG_PF) closedown);
566 		(void) alarm(_RPCSVC_CLOSEDOWN/2);
567 	}
568 /*
569  * Make sure SIGPIPE doesn't blow us away while servicing TCP
570  * connections.
571  */
572 	(void) signal(SIGPIPE, SIG_IGN);
573 	(void) signal(SIGCHLD, (SIG_PF) reaper);
574 	(void) signal(SIGTERM, (SIG_PF) reaper);
575 	(void) signal(SIGINT, (SIG_PF) reaper);
576 	(void) signal(SIGHUP, (SIG_PF) reaper);
577 	yp_svc_run();
578 	_msgout("svc_run returned");
579 	exit(1);
580 	/* NOTREACHED */
581 }
582