xref: /freebsd/usr.sbin/rpc.statd/statd.c (revision 3c2ff3b0)
1 /*
2  * Copyright (c) 1995
3  *	A.R. Gordon (andrew.gordon@net-tel.co.uk).  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 for the FreeBSD project
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 ANDREW GORDON 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 THE AUTHOR 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 
34 /* main() function for status monitor daemon.  Some of the code in this	*/
35 /* file was generated by running rpcgen /usr/include/rpcsvc/sm_inter.x	*/
36 /* The actual program logic is in the file procs.c			*/
37 
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
40 
41 #include <err.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <rpc/rpc.h>
45 #include <rpc/rpc_com.h>
46 #include <string.h>
47 #include <syslog.h>
48 #include <sys/types.h>
49 #include <sys/socket.h>
50 #include <sys/wait.h>
51 #include <netinet/in.h>
52 #include <arpa/inet.h>
53 #include <netdb.h>
54 #include <signal.h>
55 #include <unistd.h>
56 #include "statd.h"
57 
58 int debug = 0;		/* Controls syslog() calls for debug messages	*/
59 
60 char **hosts, *svcport_str = NULL;
61 int nhosts = 0;
62 int xcreated = 0;
63 
64 void create_service(struct netconfig *nconf);
65 static void handle_sigchld(int sig);
66 void out_of_mem(void);
67 
68 static void usage(void);
69 
70 int
71 main(int argc, char **argv)
72 {
73   struct sigaction sa;
74   struct netconfig *nconf;
75   void *nc_handle;
76   in_port_t svcport;
77   int ch, i, s;
78   char *endptr, **hosts_bak;
79   int have_v6 = 1;
80   int maxrec = RPC_MAXDATASIZE;
81 
82   while ((ch = getopt(argc, argv, "dh:p:")) != -1)
83     switch (ch) {
84     case 'd':
85       debug = 1;
86       break;
87     case 'h':
88       ++nhosts;
89       hosts_bak = hosts;
90       hosts_bak = realloc(hosts, nhosts * sizeof(char *));
91       if (hosts_bak == NULL) {
92 	      if (hosts != NULL) {
93 		      for (i = 0; i < nhosts; i++)
94 			      free(hosts[i]);
95 		      free(hosts);
96 		      out_of_mem();
97 	      }
98       }
99       hosts = hosts_bak;
100       hosts[nhosts - 1] = strdup(optarg);
101       if (hosts[nhosts - 1] == NULL) {
102 	      for (i = 0; i < (nhosts - 1); i++)
103 		      free(hosts[i]);
104 	      free(hosts);
105 	      out_of_mem();
106       }
107       break;
108     case 'p':
109       endptr = NULL;
110       svcport = (in_port_t)strtoul(optarg, &endptr, 10);
111       if (endptr == NULL || *endptr != '\0' || svcport == 0 ||
112           svcport >= IPPORT_MAX)
113 	usage();
114 
115       svcport_str = strdup(optarg);
116       break;
117     default:
118       usage();
119     }
120   argc -= optind;
121   argv += optind;
122 
123   (void)rpcb_unset(SM_PROG, SM_VERS, NULL);
124 
125   /*
126    * Check if IPv6 support is present.
127    */
128   s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
129   if (s < 0)
130       have_v6 = 0;
131   else
132       close(s);
133 
134   rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
135 
136   /*
137    * If no hosts were specified, add a wildcard entry to bind to
138    * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the
139    * list.
140    */
141   if (nhosts == 0) {
142 	  hosts = malloc(sizeof(char**));
143 	  if (hosts == NULL)
144 		  out_of_mem();
145 
146 	  hosts[0] = "*";
147 	  nhosts = 1;
148   } else {
149 	  hosts_bak = hosts;
150 	  if (have_v6) {
151 		  hosts_bak = realloc(hosts, (nhosts + 2) *
152 		      sizeof(char *));
153 		  if (hosts_bak == NULL) {
154 			  for (i = 0; i < nhosts; i++)
155 				  free(hosts[i]);
156 			  free(hosts);
157 			  out_of_mem();
158 		  } else
159 			  hosts = hosts_bak;
160 
161 		  nhosts += 2;
162 		  hosts[nhosts - 2] = "::1";
163 	  } else {
164 		  hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *));
165 		  if (hosts_bak == NULL) {
166 			  for (i = 0; i < nhosts; i++)
167 				  free(hosts[i]);
168 
169 			  free(hosts);
170 			  out_of_mem();
171 		  } else {
172 			  nhosts += 1;
173 			  hosts = hosts_bak;
174 		  }
175 	  }
176 	  hosts[nhosts - 1] = "127.0.0.1";
177   }
178 
179   nc_handle = setnetconfig();
180   while ((nconf = getnetconfig(nc_handle))) {
181 	  /* We want to listen only on udp6, tcp6, udp, tcp transports */
182 	  if (nconf->nc_flag & NC_VISIBLE) {
183 		  /* Skip if there's no IPv6 support */
184 		  if (have_v6 == 0 && strcmp(nconf->nc_protofmly, "inet6") == 0) {
185 	      /* DO NOTHING */
186 		  } else {
187 			  create_service(nconf);
188 		  }
189 	  }
190   }
191   endnetconfig(nc_handle);
192   init_file("/var/db/statd.status");
193 
194   /* Note that it is NOT sensible to run this program from inetd - the 	*/
195   /* protocol assumes that it will run immediately at boot time.	*/
196   daemon(0, 0);
197   openlog("rpc.statd", 0, LOG_DAEMON);
198   if (debug) syslog(LOG_INFO, "Starting - debug enabled");
199   else syslog(LOG_INFO, "Starting");
200 
201   /* Install signal handler to collect exit status of child processes	*/
202   sa.sa_handler = handle_sigchld;
203   sigemptyset(&sa.sa_mask);
204   sigaddset(&sa.sa_mask, SIGCHLD);
205   sa.sa_flags = SA_RESTART;
206   sigaction(SIGCHLD, &sa, NULL);
207 
208   /* Initialisation now complete - start operating			*/
209   notify_hosts();	/* Forks a process (if necessary) to do the	*/
210 			/* SM_NOTIFY calls, which may be slow.		*/
211 
212   svc_run();	/* Should never return					*/
213   exit(1);
214 }
215 
216 /*
217  * This routine creates and binds sockets on the appropriate
218  * addresses. It gets called one time for each transport and
219  * registrates the service with rpcbind on that trasport.
220  */
221 void
222 create_service(struct netconfig *nconf)
223 {
224 	struct addrinfo hints, *res = NULL;
225 	struct sockaddr_in *sin;
226 	struct sockaddr_in6 *sin6;
227 	struct __rpc_sockinfo si;
228 	struct netbuf servaddr;
229 	SVCXPRT	*transp = NULL;
230 	int aicode;
231 	int fd;
232 	int nhostsbak;
233 	int r;
234 	int registered = 0;
235 	u_int32_t host_addr[4];  /* IPv4 or IPv6 */
236 
237 	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
238 	    (nconf->nc_semantics != NC_TPI_COTS) &&
239 	    (nconf->nc_semantics != NC_TPI_COTS_ORD))
240 		return;	/* not my type */
241 
242 	/*
243 	 * XXX - using RPC library internal functions.
244 	 */
245 	if (!__rpc_nconf2sockinfo(nconf, &si)) {
246 		syslog(LOG_ERR, "cannot get information for %s",
247 		    nconf->nc_netid);
248 		return;
249 	}
250 
251 	/* Get rpc.statd's address on this transport */
252 	memset(&hints, 0, sizeof hints);
253 	hints.ai_flags = AI_PASSIVE;
254 	hints.ai_family = si.si_af;
255 	hints.ai_socktype = si.si_socktype;
256 	hints.ai_protocol = si.si_proto;
257 
258 	/*
259 	 * Bind to specific IPs if asked to
260 	 */
261 	nhostsbak = nhosts;
262 	while (nhostsbak > 0) {
263 		--nhostsbak;
264 
265 		/*
266 		 * XXX - using RPC library internal functions.
267 		 */
268 		if ((fd = __rpc_nconf2fd(nconf)) < 0) {
269 			syslog(LOG_ERR, "cannot create socket for %s",
270 			    nconf->nc_netid);
271 			continue;
272 		}
273 		switch (hints.ai_family) {
274 		case AF_INET:
275 			if (inet_pton(AF_INET, hosts[nhostsbak],
276 			    host_addr) == 1) {
277 				hints.ai_flags &= AI_NUMERICHOST;
278 			} else {
279 				/*
280 				 * Skip if we have an AF_INET6 address.
281 				 */
282 				if (inet_pton(AF_INET6, hosts[nhostsbak],
283 				    host_addr) == 1) {
284 					close(fd);
285 					continue;
286 				}
287 			}
288 			break;
289 		case AF_INET6:
290 			if (inet_pton(AF_INET6, hosts[nhostsbak],
291 			    host_addr) == 1) {
292 				hints.ai_flags &= AI_NUMERICHOST;
293 			} else {
294 				/*
295 				 * Skip if we have an AF_INET address.
296 				 */
297 				if (inet_pton(AF_INET, hosts[nhostsbak],
298 				    host_addr) == 1) {
299 					close(fd);
300 					continue;
301 				}
302 			}
303 			break;
304 		default:
305 			break;
306 		}
307 
308 		/*
309 		 * If no hosts were specified, just bind to INADDR_ANY
310 		 */
311 		if (strcmp("*", hosts[nhostsbak]) == 0) {
312 			if (svcport_str == NULL) {
313 				res = malloc(sizeof(struct addrinfo));
314 				if (res == NULL)
315 					out_of_mem();
316 				res->ai_flags = hints.ai_flags;
317 				res->ai_family = hints.ai_family;
318 				res->ai_protocol = hints.ai_protocol;
319 				switch (res->ai_family) {
320 				case AF_INET:
321 					sin = malloc(sizeof(struct sockaddr_in));
322 					if (sin == NULL)
323 						out_of_mem();
324 					sin->sin_family = AF_INET;
325 					sin->sin_port = htons(0);
326 					sin->sin_addr.s_addr = htonl(INADDR_ANY);
327 					res->ai_addr = (struct sockaddr*) sin;
328 					res->ai_addrlen = (socklen_t)
329 					    sizeof(res->ai_addr);
330 					break;
331 				case AF_INET6:
332 					sin6 = malloc(sizeof(struct sockaddr_in6));
333 					if (res->ai_addr == NULL)
334 						out_of_mem();
335 					sin6->sin6_family = AF_INET6;
336 					sin6->sin6_port = htons(0);
337 					sin6->sin6_addr = in6addr_any;
338 					res->ai_addr = (struct sockaddr*) sin6;
339 					res->ai_addrlen = (socklen_t) sizeof(res->ai_addr);
340 					break;
341 				default:
342 					break;
343 				}
344 			} else {
345 				if ((aicode = getaddrinfo(NULL, svcport_str,
346 				    &hints, &res)) != 0) {
347 					syslog(LOG_ERR,
348 					    "cannot get local address for %s: %s",
349 					    nconf->nc_netid,
350 					    gai_strerror(aicode));
351 					continue;
352 				}
353 			}
354 		} else {
355 			if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str,
356 			    &hints, &res)) != 0) {
357 				syslog(LOG_ERR,
358 				    "cannot get local address for %s: %s",
359 				    nconf->nc_netid, gai_strerror(aicode));
360 				continue;
361 			}
362 		}
363 
364 		r = bindresvport_sa(fd, res->ai_addr);
365 		if (r != 0) {
366 			syslog(LOG_ERR, "bindresvport_sa: %m");
367 			exit(1);
368 		}
369 
370 		transp = svc_tli_create(fd, nconf, NULL,
371 		RPC_MAXDATASIZE, RPC_MAXDATASIZE);
372 
373 		if (transp != (SVCXPRT *) NULL) {
374 			if (!svc_register(transp, SM_PROG, SM_VERS,
375 			    sm_prog_1, 0)) {
376 				syslog(LOG_ERR, "can't register on %s",
377 				    nconf->nc_netid);
378 			} else {
379 				if (!svc_reg(transp, SM_PROG, SM_VERS,
380 				    sm_prog_1, NULL))
381 					syslog(LOG_ERR,
382 					    "can't register %s SM_PROG service",
383 					    nconf->nc_netid);
384 			}
385 		} else
386 			syslog(LOG_WARNING, "can't create %s services",
387 			    nconf->nc_netid);
388 
389 		if (registered == 0) {
390 			registered = 1;
391 			memset(&hints, 0, sizeof hints);
392 			hints.ai_flags = AI_PASSIVE;
393 			hints.ai_family = si.si_af;
394 			hints.ai_socktype = si.si_socktype;
395 			hints.ai_protocol = si.si_proto;
396 
397 			if (svcport_str == NULL) {
398 				svcport_str = malloc(NI_MAXSERV * sizeof(char));
399 				if (svcport_str == NULL)
400 					out_of_mem();
401 
402 				if (getnameinfo(res->ai_addr,
403 				    res->ai_addr->sa_len, NULL, NI_MAXHOST,
404 				    svcport_str, NI_MAXSERV * sizeof(char),
405 				    NI_NUMERICHOST | NI_NUMERICSERV))
406 					errx(1, "Cannot get port number");
407 			}
408 
409 			if((aicode = getaddrinfo(NULL, svcport_str, &hints,
410 			    &res)) != 0) {
411 				syslog(LOG_ERR, "cannot get local address: %s",
412 				    gai_strerror(aicode));
413 				exit(1);
414 			}
415 
416 			servaddr.buf = malloc(res->ai_addrlen);
417 			memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen);
418 			servaddr.len = res->ai_addrlen;
419 
420 			rpcb_set(SM_PROG, SM_VERS, nconf, &servaddr);
421 
422 			xcreated++;
423 			freeaddrinfo(res);
424 		}
425 	} /* end while */
426 }
427 
428 static void
429 usage()
430 {
431       fprintf(stderr, "usage: rpc.statd [-d] [-h <bindip>] [-p <port>]\n");
432       exit(1);
433 }
434 
435 /* handle_sigchld ---------------------------------------------------------- */
436 /*
437    Purpose:	Catch SIGCHLD and collect process status
438    Retruns:	Nothing.
439    Notes:	No special action required, other than to collect the
440 		process status and hence allow the child to die:
441 		we only use child processes for asynchronous transmission
442 		of SM_NOTIFY to other systems, so it is normal for the
443 		children to exit when they have done their work.
444 */
445 
446 static void handle_sigchld(int sig __unused)
447 {
448   int pid, status;
449   pid = wait4(-1, &status, WNOHANG, (struct rusage*)0);
450   if (!pid) syslog(LOG_ERR, "Phantom SIGCHLD??");
451   else if (status == 0)
452   {
453     if (debug) syslog(LOG_DEBUG, "Child %d exited OK", pid);
454   }
455   else syslog(LOG_ERR, "Child %d failed with status %d", pid,
456     WEXITSTATUS(status));
457 }
458 
459 /*
460  * Out of memory, fatal
461  */
462 void
463 out_of_mem()
464 {
465 
466 	syslog(LOG_ERR, "out of memory");
467 	exit(2);
468 }
469