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