xref: /freebsd/usr.sbin/ypserv/yp_main.c (revision eb6d21b4ca6d668cf89afd99eef7baeafa712197)
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 (bind(s, res->ai_addr,
307 				    res->ai_addrlen) == -1) {
308 					_msgout("cannot bind %s socket: %s",
309 					    nconf->nc_netid, strerror(errno));
310 					freeaddrinfo(res0);
311 					close(sock);
312 					return -1;
313 				}
314 				if (nconf->nc_semantics != NC_TPI_CLTS)
315 					listen(s, SOMAXCONN);
316 
317 				slep = malloc(sizeof(*slep));
318 				if (slep == NULL) {
319 					_msgout("malloc failed: %s",
320 					    strerror(errno));
321 					freeaddrinfo(res0);
322 					close(s);
323 					return -1;
324 				}
325 				memset(slep, 0, sizeof(*slep));
326 				memcpy(&slep->sle_ss,
327 				    (struct sockaddr *)(res->ai_addr),
328 				    sizeof(res->ai_addr));
329 				slep->sle_sock = s;
330 				SLIST_INSERT_HEAD(&sle_head, slep, sle_next);
331 
332 				/*
333 				 * If servname == "0", redefine it by using
334 				 * the bound socket.
335 				 */
336 				if (strncmp("0", servname, 1) == 0) {
337 					struct sockaddr *sap;
338 					socklen_t slen;
339 					char *sname;
340 
341 					sname = malloc(NI_MAXSERV);
342 					if (sname == NULL) {
343 						_msgout("malloc(): %s",
344 						    strerror(errno));
345 						freeaddrinfo(res0);
346 						close(s);
347 						return -1;
348 					}
349 					memset(sname, 0, NI_MAXSERV);
350 
351 					sap = (struct sockaddr *)&slep->sle_ss;
352 					slen = sizeof(*sap);
353 					error = getsockname(s, sap, &slen);
354 					if (error) {
355 						_msgout("getsockname(): %s",
356 						    strerror(errno));
357 						freeaddrinfo(res0);
358 						close(s);
359 						return -1;
360 					}
361 					error = getnameinfo(sap, slen,
362 					    NULL, 0,
363 					    sname, NI_MAXSERV,
364 					    NI_NUMERICHOST | NI_NUMERICSERV);
365 					if (error) {
366 						_msgout("getnameinfo(): %s",
367 						    strerror(errno));
368 						freeaddrinfo(res0);
369 						close(s);
370 						return -1;
371 					}
372 					servname = sname;
373 				}
374 			}
375 			freeaddrinfo(res0);
376 		}
377 	} else {
378 		slep = malloc(sizeof(*slep));
379 		if (slep == NULL) {
380 			_msgout("malloc failed: %s", strerror(errno));
381 			return -1;
382 		}
383 		memset(slep, 0, sizeof(*slep));
384 		slep->sle_sock = sock;
385 		SLIST_INSERT_HEAD(&sle_head, slep, sle_next);
386 	}
387 
388 	/*
389 	 * Traverse socketlist and create rpc service handles for each socket.
390 	 */
391 	SLIST_FOREACH(slep, &sle_head, sle_next) {
392 		if (nconf->nc_semantics == NC_TPI_CLTS)
393 			transp = svc_dg_create(slep->sle_sock, 0, 0);
394 		else
395 			transp = svc_vc_create(slep->sle_sock, RPC_MAXDATASIZE,
396 			    RPC_MAXDATASIZE);
397 		if (transp == NULL) {
398 			_msgout("unable to create service: %s",
399 			    nconf->nc_netid);
400 			continue;
401 		}
402 		if (!svc_reg(transp, YPPROG, YPOLDVERS, ypprog_1, NULL)) {
403 			svc_destroy(transp);
404 			close(slep->sle_sock);
405 			_msgout("unable to register (YPPROG, YPOLDVERS, %s):"
406 			    " %s", nconf->nc_netid, strerror(errno));
407 			continue;
408 		}
409 		if (!svc_reg(transp, YPPROG, YPVERS, ypprog_2, NULL)) {
410 			svc_destroy(transp);
411 			close(slep->sle_sock);
412 			_msgout("unable to register (YPPROG, YPVERS, %s): %s",
413 			    nconf->nc_netid, strerror(errno));
414 			continue;
415 		}
416 	}
417 	while(!(SLIST_EMPTY(&sle_head)))
418 		SLIST_REMOVE_HEAD(&sle_head, sle_next);
419 
420 	/*
421 	 * Register RPC service to rpcbind by using AI_PASSIVE address.
422 	 */
423 	hints.ai_flags = AI_PASSIVE;
424 	error = getaddrinfo(NULL, servname, &hints, &res0);
425 	if (error) {
426 		_msgout("getaddrinfo(): %s", gai_strerror(error));
427 		return -1;
428 	}
429 	svcaddr.buf = res0->ai_addr;
430 	svcaddr.len = res0->ai_addrlen;
431 
432 	if (si->si_af == AF_INET) {
433 		/* XXX: ignore error intentionally */
434 		rpcb_set(YPPROG, YPOLDVERS, nconf, &svcaddr);
435 	}
436 	/* XXX: ignore error intentionally */
437 	rpcb_set(YPPROG, YPVERS, nconf, &svcaddr);
438 
439 	freeaddrinfo(res0);
440 	return 0;
441 }
442 
443 int
444 main(int argc, char *argv[])
445 {
446 	int ch;
447 	int error;
448 
449 	void *nc_handle;
450 	struct netconfig *nconf;
451 	struct __rpc_sockinfo si;
452 	struct bindaddrlistent *blep;
453 
454 	memset(&si, 0, sizeof(si));
455 	SLIST_INIT(&ble_head);
456 
457 	while ((ch = getopt(argc, argv, "dh:np:P:")) != -1) {
458 		switch (ch) {
459 		case 'd':
460 			debug = ypdb_debug = 1;
461 			break;
462 		case 'h':
463 			blep = malloc(sizeof(*blep));
464 			if (blep == NULL)
465 				err(1, "malloc() failed: -h %s", optarg);
466 			blep->ble_hostname = optarg;
467 			SLIST_INSERT_HEAD(&ble_head, blep, ble_next);
468 			break;
469 		case 'n':
470 			do_dns = 1;
471 			break;
472 		case 'p':
473 			yp_dir = optarg;
474 			break;
475 		case 'P':
476 			servname = optarg;
477 			break;
478 		default:
479 			usage();
480 		}
481 	}
482 	/*
483 	 * Add "anyaddr" entry if no -h is specified.
484 	 */
485 	if (SLIST_EMPTY(&ble_head)) {
486 		blep = malloc(sizeof(*blep));
487 		if (blep == NULL)
488 			err(1, "malloc() failed");
489 		memset(blep, 0, sizeof(*blep));
490 		SLIST_INSERT_HEAD(&ble_head, blep, ble_next);
491 	}
492 
493 	load_securenets();
494 	yp_init_resolver();
495 #ifdef DB_CACHE
496 	yp_init_dbs();
497 #endif
498 	nc_handle = setnetconfig();
499 	if (nc_handle == NULL)
500 		err(1, "cannot read %s", NETCONFIG);
501 	if (__rpc_fd2sockinfo(0, &si) != 0) {
502 		/* invoked from inetd */
503 		_rpcpmstart = 1;
504 		_rpcfdtype = si.si_socktype;
505 		_rpcaf = si.si_af;
506 		_rpcfd = 0;
507 		openlog("ypserv", LOG_PID, LOG_DAEMON);
508 	} else {
509 		/* standalone mode */
510 		if (!debug) {
511 			if (daemon(0,0)) {
512 				err(1,"cannot fork");
513 			}
514 			openlog("ypserv", LOG_PID, LOG_DAEMON);
515 		}
516 		_rpcpmstart = 0;
517 		_rpcaf = AF_INET;
518 		_rpcfd = RPC_ANYFD;
519 		unregister();
520 	}
521 
522 	/*
523 	 * Create RPC service for each transport.
524 	 */
525 	while((nconf = getnetconfig(nc_handle))) {
526 		if ((nconf->nc_flag & NC_VISIBLE)) {
527 			if (__rpc_nconf2sockinfo(nconf, &si) == 0) {
528 				_msgout("cannot get information for %s",
529 				    nconf->nc_netid);
530 				exit(1);
531 			}
532 			if (_rpcpmstart) {
533 				if (si.si_socktype != _rpcfdtype ||
534 				    si.si_af != _rpcaf)
535 					continue;
536 			} else if (si.si_af != _rpcaf)
537 					continue;
538 			error = create_service(_rpcfd, nconf, &si);
539 			if (error) {
540 				endnetconfig(nc_handle);
541 				exit(1);
542 			}
543 		}
544 	}
545 	endnetconfig(nc_handle);
546 	while(!(SLIST_EMPTY(&ble_head)))
547 		SLIST_REMOVE_HEAD(&ble_head, ble_next);
548 
549 	if (_rpcpmstart) {
550 		(void) signal(SIGALRM, (SIG_PF) closedown);
551 		(void) alarm(_RPCSVC_CLOSEDOWN/2);
552 	}
553 /*
554  * Make sure SIGPIPE doesn't blow us away while servicing TCP
555  * connections.
556  */
557 	(void) signal(SIGPIPE, SIG_IGN);
558 	(void) signal(SIGCHLD, (SIG_PF) reaper);
559 	(void) signal(SIGTERM, (SIG_PF) reaper);
560 	(void) signal(SIGINT, (SIG_PF) reaper);
561 	(void) signal(SIGHUP, (SIG_PF) reaper);
562 	yp_svc_run();
563 	_msgout("svc_run returned");
564 	exit(1);
565 	/* NOTREACHED */
566 }
567