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