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