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 } 437