xref: /freebsd/usr.sbin/ypserv/yp_main.c (revision f05cddf9)
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/mman.h>
45 #include <sys/queue.h>
46 #include <sys/socket.h>
47 #include <sys/wait.h>
48 #include "yp.h"
49 #include <err.h>
50 #include <errno.h>
51 #include <memory.h>
52 #include <stdio.h>
53 #include <signal.h>
54 #include <stdarg.h>
55 #include <stdlib.h> /* getenv, exit */
56 #include <string.h> /* strcmp */
57 #include <syslog.h>
58 #include <unistd.h>
59 #ifdef __cplusplus
60 #include <sysent.h> /* getdtablesize, open */
61 #endif /* __cplusplus */
62 #include <netinet/in.h>
63 #include <netdb.h>
64 #include "yp_extern.h"
65 #include <netconfig.h>
66 #include <rpc/rpc.h>
67 #include <rpc/rpc_com.h>
68 
69 #ifndef SIG_PF
70 #define	SIG_PF void(*)(int)
71 #endif
72 
73 #define	_RPCSVC_CLOSEDOWN 120
74 int _rpcpmstart;		/* Started by a port monitor ? */
75 static int _rpcfdtype;
76 		 /* Whether Stream or Datagram ? */
77 static int _rpcaf;
78 static int _rpcfd;
79 
80 	/* States a server can be in wrt request */
81 
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 = 0;*/
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,
333 				    (struct sockaddr *)(res->ai_addr),
334 				    sizeof(res->ai_addr));
335 				slep->sle_sock = s;
336 				SLIST_INSERT_HEAD(&sle_head, slep, sle_next);
337 
338 				/*
339 				 * If servname == "0", redefine it by using
340 				 * the bound socket.
341 				 */
342 				if (strncmp("0", servname, 1) == 0) {
343 					struct sockaddr *sap;
344 					socklen_t slen;
345 					char *sname;
346 
347 					sname = malloc(NI_MAXSERV);
348 					if (sname == NULL) {
349 						_msgout("malloc(): %s",
350 						    strerror(errno));
351 						freeaddrinfo(res0);
352 						close(s);
353 						return -1;
354 					}
355 					memset(sname, 0, NI_MAXSERV);
356 
357 					sap = (struct sockaddr *)&slep->sle_ss;
358 					slen = sizeof(*sap);
359 					error = getsockname(s, sap, &slen);
360 					if (error) {
361 						_msgout("getsockname(): %s",
362 						    strerror(errno));
363 						freeaddrinfo(res0);
364 						close(s);
365 						free(sname);
366 						return -1;
367 					}
368 					error = getnameinfo(sap, slen,
369 					    NULL, 0,
370 					    sname, NI_MAXSERV,
371 					    NI_NUMERICHOST | NI_NUMERICSERV);
372 					if (error) {
373 						_msgout("getnameinfo(): %s",
374 						    strerror(errno));
375 						freeaddrinfo(res0);
376 						close(s);
377 						free(sname);
378 						return -1;
379 					}
380 					servname = sname;
381 				}
382 			}
383 			freeaddrinfo(res0);
384 		}
385 	} else {
386 		slep = malloc(sizeof(*slep));
387 		if (slep == NULL) {
388 			_msgout("malloc failed: %s", strerror(errno));
389 			return -1;
390 		}
391 		memset(slep, 0, sizeof(*slep));
392 		slep->sle_sock = sock;
393 		SLIST_INSERT_HEAD(&sle_head, slep, sle_next);
394 	}
395 
396 	/*
397 	 * Traverse socketlist and create rpc service handles for each socket.
398 	 */
399 	SLIST_FOREACH(slep, &sle_head, sle_next) {
400 		if (nconf->nc_semantics == NC_TPI_CLTS)
401 			transp = svc_dg_create(slep->sle_sock, 0, 0);
402 		else
403 			transp = svc_vc_create(slep->sle_sock, RPC_MAXDATASIZE,
404 			    RPC_MAXDATASIZE);
405 		if (transp == NULL) {
406 			_msgout("unable to create service: %s",
407 			    nconf->nc_netid);
408 			continue;
409 		}
410 		if (!svc_reg(transp, YPPROG, YPOLDVERS, ypprog_1, NULL)) {
411 			svc_destroy(transp);
412 			close(slep->sle_sock);
413 			_msgout("unable to register (YPPROG, YPOLDVERS, %s):"
414 			    " %s", nconf->nc_netid, strerror(errno));
415 			continue;
416 		}
417 		if (!svc_reg(transp, YPPROG, YPVERS, ypprog_2, NULL)) {
418 			svc_destroy(transp);
419 			close(slep->sle_sock);
420 			_msgout("unable to register (YPPROG, YPVERS, %s): %s",
421 			    nconf->nc_netid, strerror(errno));
422 			continue;
423 		}
424 	}
425 	while(!(SLIST_EMPTY(&sle_head)))
426 		SLIST_REMOVE_HEAD(&sle_head, sle_next);
427 
428 	/*
429 	 * Register RPC service to rpcbind by using AI_PASSIVE address.
430 	 */
431 	hints.ai_flags = AI_PASSIVE;
432 	error = getaddrinfo(NULL, servname, &hints, &res0);
433 	if (error) {
434 		_msgout("getaddrinfo(): %s", gai_strerror(error));
435 		return -1;
436 	}
437 	svcaddr.buf = res0->ai_addr;
438 	svcaddr.len = res0->ai_addrlen;
439 
440 	if (si->si_af == AF_INET) {
441 		/* XXX: ignore error intentionally */
442 		rpcb_set(YPPROG, YPOLDVERS, nconf, &svcaddr);
443 	}
444 	/* XXX: ignore error intentionally */
445 	rpcb_set(YPPROG, YPVERS, nconf, &svcaddr);
446 	freeaddrinfo(res0);
447 	return 0;
448 }
449 
450 int
451 main(int argc, char *argv[])
452 {
453 	int ch;
454 	int error;
455 	int ntrans;
456 
457 	void *nc_handle;
458 	struct netconfig *nconf;
459 	struct __rpc_sockinfo si;
460 	struct bindaddrlistent *blep;
461 
462 	memset(&si, 0, sizeof(si));
463 	SLIST_INIT(&ble_head);
464 
465 	while ((ch = getopt(argc, argv, "dh:np:P:")) != -1) {
466 		switch (ch) {
467 		case 'd':
468 			debug = ypdb_debug = 1;
469 			break;
470 		case 'h':
471 			blep = malloc(sizeof(*blep));
472 			if (blep == NULL)
473 				err(1, "malloc() failed: -h %s", optarg);
474 			blep->ble_hostname = optarg;
475 			SLIST_INSERT_HEAD(&ble_head, blep, ble_next);
476 			break;
477 		case 'n':
478 			do_dns = 1;
479 			break;
480 		case 'p':
481 			yp_dir = optarg;
482 			break;
483 		case 'P':
484 			servname = optarg;
485 			break;
486 		default:
487 			usage();
488 		}
489 	}
490 	/*
491 	 * Add "anyaddr" entry if no -h is specified.
492 	 */
493 	if (SLIST_EMPTY(&ble_head)) {
494 		blep = malloc(sizeof(*blep));
495 		if (blep == NULL)
496 			err(1, "malloc() failed");
497 		memset(blep, 0, sizeof(*blep));
498 		SLIST_INSERT_HEAD(&ble_head, blep, ble_next);
499 	}
500 
501 	load_securenets();
502 	yp_init_resolver();
503 #ifdef DB_CACHE
504 	yp_init_dbs();
505 #endif
506 	nc_handle = setnetconfig();
507 	if (nc_handle == NULL)
508 		err(1, "cannot read %s", NETCONFIG);
509 	if (__rpc_fd2sockinfo(0, &si) != 0) {
510 		/* invoked from inetd */
511 		_rpcpmstart = 1;
512 		_rpcfdtype = si.si_socktype;
513 		_rpcaf = si.si_af;
514 		_rpcfd = 0;
515 		openlog("ypserv", LOG_PID, LOG_DAEMON);
516 	} else {
517 		/* standalone mode */
518 		if (!debug) {
519 			if (daemon(0,0)) {
520 				err(1,"cannot fork");
521 			}
522 			openlog("ypserv", LOG_PID, LOG_DAEMON);
523 		}
524 		_rpcpmstart = 0;
525 		_rpcaf = AF_INET;
526 		_rpcfd = RPC_ANYFD;
527 		unregister();
528 	}
529 
530 	if (madvise(NULL, 0, MADV_PROTECT) != 0)
531 		_msgout("madvise(): %s", strerror(errno));
532 
533 	/*
534 	 * Create RPC service for each transport.
535 	 */
536 	ntrans = 0;
537 	while((nconf = getnetconfig(nc_handle))) {
538 		if ((nconf->nc_flag & NC_VISIBLE)) {
539 			if (__rpc_nconf2sockinfo(nconf, &si) == 0) {
540 				_msgout("cannot get information for %s.  "
541 				    "Ignored.", nconf->nc_netid);
542 				continue;
543 			}
544 			if (_rpcpmstart) {
545 				if (si.si_socktype != _rpcfdtype ||
546 				    si.si_af != _rpcaf)
547 					continue;
548 			} else if (si.si_af != _rpcaf)
549 					continue;
550 			error = create_service(_rpcfd, nconf, &si);
551 			if (error) {
552 				endnetconfig(nc_handle);
553 				exit(1);
554 			}
555 			ntrans++;
556 		}
557 	}
558 	endnetconfig(nc_handle);
559 	while(!(SLIST_EMPTY(&ble_head)))
560 		SLIST_REMOVE_HEAD(&ble_head, ble_next);
561 	if (ntrans == 0) {
562 		_msgout("no transport is available.  Aborted.");
563 		exit(1);
564 	}
565 	if (_rpcpmstart) {
566 		(void) signal(SIGALRM, (SIG_PF) closedown);
567 		(void) alarm(_RPCSVC_CLOSEDOWN/2);
568 	}
569 /*
570  * Make sure SIGPIPE doesn't blow us away while servicing TCP
571  * connections.
572  */
573 	(void) signal(SIGPIPE, SIG_IGN);
574 	(void) signal(SIGCHLD, (SIG_PF) reaper);
575 	(void) signal(SIGTERM, (SIG_PF) reaper);
576 	(void) signal(SIGINT, (SIG_PF) reaper);
577 	(void) signal(SIGHUP, (SIG_PF) reaper);
578 	yp_svc_run();
579 	_msgout("svc_run returned");
580 	exit(1);
581 	/* NOTREACHED */
582 }
583