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