1 /*-
2 * SPDX-License-Identifier: BSD-4-Clause
3 *
4 * Copyright (c) 1995
5 * A.R. Gordon ([email protected]). All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed for the FreeBSD project
18 * 4. Neither the name of the author nor the names of any co-contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 */
35
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD$");
38
39 #include <errno.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <rpc/rpc.h>
45 #include <syslog.h>
46 #include <vis.h>
47 #include <netdb.h> /* for getaddrinfo() */
48 #include <sys/types.h>
49 #include <sys/socket.h>
50 #include <netinet/in.h>
51 #include <arpa/inet.h>
52
53 #include "statd.h"
54
55 static const char *from_addr(struct sockaddr *);
56
57 static const char *
from_addr(struct sockaddr * saddr)58 from_addr(struct sockaddr *saddr)
59 {
60 static char inet_buf[INET6_ADDRSTRLEN];
61
62 if (getnameinfo(saddr, saddr->sa_len, inet_buf, sizeof(inet_buf),
63 NULL, 0, NI_NUMERICHOST) == 0)
64 return inet_buf;
65 return "???";
66 }
67
68 /* sm_check_hostname -------------------------------------------------------- */
69 /*
70 * Purpose: Check `mon_name' member of sm_name struct to ensure that the array
71 * consists only of printable characters.
72 *
73 * Returns: TRUE if hostname is good. FALSE if hostname contains binary or
74 * otherwise non-printable characters.
75 *
76 * Notes: Will syslog(3) to warn of corrupt hostname.
77 */
78
sm_check_hostname(struct svc_req * req,char * arg)79 int sm_check_hostname(struct svc_req *req, char *arg)
80 {
81 int len, dstlen, ret;
82 struct sockaddr *claddr;
83 char *dst;
84
85 len = strlen(arg);
86 dstlen = (4 * len) + 1;
87 dst = malloc(dstlen);
88 claddr = (struct sockaddr *) (svc_getrpccaller(req->rq_xprt)->buf) ;
89 ret = 1;
90
91 if (claddr == NULL || dst == NULL)
92 {
93 ret = 0;
94 }
95 else if (strvis(dst, arg, VIS_WHITE) != len)
96 {
97 syslog(LOG_ERR,
98 "sm_stat: client %s hostname %s contained invalid characters.",
99 from_addr(claddr),
100 dst);
101 ret = 0;
102 }
103 free(dst);
104 return (ret);
105 }
106
107 /* sm_stat_1 --------------------------------------------------------------- */
108 /*
109 Purpose: RPC call to enquire if a host can be monitored
110 Returns: TRUE for any hostname that can be looked up to give
111 an address.
112 */
113
sm_stat_1_svc(sm_name * arg,struct svc_req * req)114 struct sm_stat_res *sm_stat_1_svc(sm_name *arg, struct svc_req *req)
115 {
116 static sm_stat_res res;
117 struct addrinfo *ai;
118 struct sockaddr *claddr;
119 static int err;
120
121 err = 1;
122 if ((err = sm_check_hostname(req, arg->mon_name)) == 0)
123 {
124 res.res_stat = stat_fail;
125 }
126 if (err != 0)
127 {
128 if (debug)
129 syslog(LOG_DEBUG, "stat called for host %s", arg->mon_name);
130 if (getaddrinfo(arg->mon_name, NULL, NULL, &ai) == 0) {
131 res.res_stat = stat_succ;
132 freeaddrinfo(ai);
133 }
134 else
135 {
136 claddr = (struct sockaddr *) (svc_getrpccaller(req->rq_xprt)->buf) ;
137 syslog(LOG_ERR, "invalid hostname to sm_stat from %s: %s",
138 from_addr(claddr), arg->mon_name);
139 res.res_stat = stat_fail;
140 }
141 }
142 res.state = status_info->ourState;
143 return (&res);
144 }
145
146 /* sm_mon_1 ---------------------------------------------------------------- */
147 /*
148 Purpose: RPC procedure to establish a monitor request
149 Returns: Success, unless lack of resources prevents
150 the necessary structures from being set up
151 to record the request, or if the hostname is not
152 valid (as judged by getaddrinfo())
153 */
154
sm_mon_1_svc(mon * arg,struct svc_req * req)155 struct sm_stat_res *sm_mon_1_svc(mon *arg, struct svc_req *req)
156 {
157 static sm_stat_res res;
158 HostInfo *hp;
159 static int err;
160 MonList *lp;
161 struct addrinfo *ai;
162
163 if ((err = sm_check_hostname(req, arg->mon_id.mon_name)) == 0)
164 {
165 res.res_stat = stat_fail;
166 }
167
168 if (err != 0)
169 {
170 if (debug)
171 {
172 syslog(LOG_DEBUG, "monitor request for host %s", arg->mon_id.mon_name);
173 syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
174 arg->mon_id.my_id.my_name,
175 arg->mon_id.my_id.my_prog,
176 arg->mon_id.my_id.my_vers,
177 arg->mon_id.my_id.my_proc);
178 }
179 res.res_stat = stat_fail; /* Assume fail until set otherwise */
180 res.state = status_info->ourState;
181
182 /* Find existing host entry, or create one if not found */
183 /* If find_host() fails, it will have logged the error already. */
184 if (getaddrinfo(arg->mon_id.mon_name, NULL, NULL, &ai) != 0)
185 {
186 syslog(LOG_ERR, "Invalid hostname to sm_mon: %s", arg->mon_id.mon_name);
187 return (&res);
188 }
189 freeaddrinfo(ai);
190 if ((hp = find_host(arg->mon_id.mon_name, TRUE)))
191 {
192 lp = (MonList *)malloc(sizeof(MonList));
193 if (!lp)
194 {
195 syslog(LOG_ERR, "Out of memory");
196 }
197 else
198 {
199 strncpy(lp->notifyHost, arg->mon_id.my_id.my_name, SM_MAXSTRLEN);
200 lp->notifyProg = arg->mon_id.my_id.my_prog;
201 lp->notifyVers = arg->mon_id.my_id.my_vers;
202 lp->notifyProc = arg->mon_id.my_id.my_proc;
203 memcpy(lp->notifyData, arg->priv, sizeof(lp->notifyData));
204
205 lp->next = hp->monList;
206 hp->monList = lp;
207 sync_file();
208
209 res.res_stat = stat_succ; /* Report success */
210 }
211 }
212 }
213 return (&res);
214 }
215
216 /* do_unmon ---------------------------------------------------------------- */
217 /*
218 Purpose: Remove a monitor request from a host
219 Returns: TRUE if found, FALSE if not found.
220 Notes: Common code from sm_unmon_1_svc and sm_unmon_all_1_svc
221 In the unlikely event of more than one identical monitor
222 request, all are removed.
223 */
224
do_unmon(HostInfo * hp,my_id * idp)225 static int do_unmon(HostInfo *hp, my_id *idp)
226 {
227 MonList *lp, *next;
228 MonList *last = NULL;
229 int result = FALSE;
230
231 lp = hp->monList;
232 while (lp)
233 {
234 if (!strncasecmp(idp->my_name, lp->notifyHost, SM_MAXSTRLEN)
235 && (idp->my_prog == lp->notifyProg) && (idp->my_proc == lp->notifyProc)
236 && (idp->my_vers == lp->notifyVers))
237 {
238 /* found one. Unhook from chain and free. */
239 next = lp->next;
240 if (last) last->next = next;
241 else hp->monList = next;
242 free(lp);
243 lp = next;
244 result = TRUE;
245 }
246 else
247 {
248 last = lp;
249 lp = lp->next;
250 }
251 }
252 return (result);
253 }
254
255 /* sm_unmon_1 -------------------------------------------------------------- */
256 /*
257 Purpose: RPC procedure to release a monitor request.
258 Returns: Local machine's status number
259 Notes: The supplied mon_id should match the value passed in an
260 earlier call to sm_mon_1
261 */
262
sm_unmon_1_svc(mon_id * arg,struct svc_req * req __unused)263 struct sm_stat *sm_unmon_1_svc(mon_id *arg, struct svc_req *req __unused)
264 {
265 static sm_stat res;
266 HostInfo *hp;
267
268 if (debug)
269 {
270 syslog(LOG_DEBUG, "un-monitor request for host %s", arg->mon_name);
271 syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
272 arg->mon_name,
273 arg->my_id.my_prog, arg->my_id.my_vers, arg->my_id.my_proc);
274 }
275
276 if ((hp = find_host(arg->mon_name, FALSE)))
277 {
278 if (do_unmon(hp, &arg->my_id)) sync_file();
279 else
280 {
281 syslog(LOG_ERR, "unmon request from %s, no matching monitor",
282 arg->my_id.my_name);
283 }
284 }
285 else syslog(LOG_ERR, "unmon request from %s for unknown host %s",
286 arg->my_id.my_name, arg->mon_name);
287
288 res.state = status_info->ourState;
289
290 return (&res);
291 }
292
293 /* sm_unmon_all_1 ---------------------------------------------------------- */
294 /*
295 Purpose: RPC procedure to release monitor requests.
296 Returns: Local machine's status number
297 Notes: Releases all monitor requests (if any) from the specified
298 host and program number.
299 */
300
sm_unmon_all_1_svc(my_id * arg,struct svc_req * req __unused)301 struct sm_stat *sm_unmon_all_1_svc(my_id *arg, struct svc_req *req __unused)
302 {
303 static sm_stat res;
304 HostInfo *hp;
305 int i;
306
307 if (debug)
308 {
309 syslog(LOG_DEBUG, "unmon_all for host: %s prog: %d ver: %d proc: %d",
310 arg->my_name, arg->my_prog, arg->my_vers, arg->my_proc);
311 }
312
313 for (i = status_info->noOfHosts, hp = status_info->hosts; i; i--, hp++)
314 {
315 do_unmon(hp, arg);
316 }
317 sync_file();
318
319 res.state = status_info->ourState;
320
321 return (&res);
322 }
323
324 /* sm_simu_crash_1 --------------------------------------------------------- */
325 /*
326 Purpose: RPC procedure to simulate a crash
327 Returns: Nothing
328 Notes: Standardised mechanism for debug purposes
329 The specification says that we should drop all of our
330 status information (apart from the list of monitored hosts
331 on disc). However, this would confuse the rpc.lockd
332 which would be unaware that all of its monitor requests
333 had been silently junked. Hence we in fact retain all
334 current requests and simply increment the status counter
335 and inform all hosts on the monitor list.
336 */
337
sm_simu_crash_1_svc(void * v __unused,struct svc_req * req __unused)338 void *sm_simu_crash_1_svc(void *v __unused, struct svc_req *req __unused)
339 {
340 static char dummy;
341 int work_to_do;
342 HostInfo *hp;
343 int i;
344
345 work_to_do = FALSE;
346 if (debug) syslog(LOG_DEBUG, "simu_crash called!!");
347
348 /* Simulate crash by setting notify-required flag on all monitored */
349 /* hosts, and incrementing our status number. notify_hosts() is */
350 /* then called to fork a process to do the notifications. */
351
352 for (i = status_info->noOfHosts, hp = status_info->hosts; i ; i--, hp++)
353 {
354 if (hp->monList)
355 {
356 work_to_do = TRUE;
357 hp->notifyReqd = TRUE;
358 }
359 }
360 status_info->ourState += 2; /* always even numbers if not crashed */
361
362 if (work_to_do) notify_hosts();
363
364 return (&dummy);
365 }
366
367 /* sm_notify_1 ------------------------------------------------------------- */
368 /*
369 Purpose: RPC procedure notifying local statd of the crash of another
370 Returns: Nothing
371 Notes: There is danger of deadlock, since it is quite likely that
372 the client procedure that we call will in turn call us
373 to remove or adjust the monitor request.
374 We therefore fork() a process to do the notifications.
375 Note that the main HostInfo structure is in a mmap()
376 region and so will be shared with the child, but the
377 monList pointed to by the HostInfo is in normal memory.
378 Hence if we read the monList before forking, we are
379 protected from the parent servicing other requests
380 that modify the list.
381 */
382
sm_notify_1_svc(stat_chge * arg,struct svc_req * req __unused)383 void *sm_notify_1_svc(stat_chge *arg, struct svc_req *req __unused)
384 {
385 struct timeval timeout = { 20, 0 }; /* 20 secs timeout */
386 CLIENT *cli;
387 static char dummy;
388 sm_status tx_arg; /* arg sent to callback procedure */
389 MonList *lp;
390 HostInfo *hp;
391 pid_t pid;
392
393 if (debug) syslog(LOG_DEBUG, "notify from host %s, new state %d",
394 arg->mon_name, arg->state);
395
396 hp = find_host(arg->mon_name, FALSE);
397 if (!hp)
398 {
399 /* Never heard of this host - why is it notifying us? */
400 syslog(LOG_ERR, "Unsolicited notification from host %s", arg->mon_name);
401 return (&dummy);
402 }
403 lp = hp->monList;
404 if (!lp) return (&dummy); /* We know this host, but have no */
405 /* outstanding requests. */
406 pid = fork();
407 if (pid == -1)
408 {
409 syslog(LOG_ERR, "Unable to fork notify process - %s", strerror(errno));
410 return (NULL); /* no answer, the client will retry */
411 }
412 if (pid) return (&dummy); /* Parent returns */
413
414 while (lp)
415 {
416 tx_arg.mon_name = arg->mon_name;
417 tx_arg.state = arg->state;
418 memcpy(tx_arg.priv, lp->notifyData, sizeof(tx_arg.priv));
419 cli = clnt_create(lp->notifyHost, lp->notifyProg, lp->notifyVers, "udp");
420 if (!cli)
421 {
422 syslog(LOG_ERR, "Failed to contact host %s%s", lp->notifyHost,
423 clnt_spcreateerror(""));
424 }
425 else
426 {
427 if (clnt_call(cli, lp->notifyProc, (xdrproc_t)xdr_sm_status, &tx_arg,
428 (xdrproc_t)xdr_void, &dummy, timeout) != RPC_SUCCESS)
429 {
430 syslog(LOG_ERR, "Failed to call rpc.statd client at host %s",
431 lp->notifyHost);
432 }
433 clnt_destroy(cli);
434 }
435 lp = lp->next;
436 }
437
438 exit (0); /* Child quits */
439 }
440