#ifndef lint
static const char rcsid[] = "$FreeBSD$";
#endif
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <search.h>
#include <oncrpc/rpc.h>
#include <syslog.h>
#include <vis.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "statd.h"
static char *
addrstr(struct sockaddr *saddr)
{
static char addrbuf[NI_MAXHOST];
if (getnameinfo(saddr, saddr->sa_len, addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST) != 0)
strlcpy(addrbuf, "<unknown>", sizeof(addrbuf));
return addrbuf;
}
int
sm_check_hostname(struct svc_req * req, char *arg)
{
int len, dstlen, ret;
struct sockaddr *claddr;
char *dst;
len = strlen(arg);
dstlen = (4 * len) + 1;
dst = malloc(dstlen);
claddr = svc_getcaller_sa(req->rq_xprt);
ret = 1;
if (claddr == NULL || dst == NULL) {
ret = 0;
} else if (strvis(dst, arg, VIS_WHITE) != len) {
log(LOG_ERR, "sm_stat: client %s hostname %s contained invalid characters.",
addrstr(claddr), dst);
ret = 0;
}
free(dst);
return (ret);
}
struct sm_stat_res *
sm_stat_1_svc(sm_name * arg, struct svc_req * req)
{
static sm_stat_res res;
struct addrinfo *ai;
static int err;
err = 1;
if ((err = sm_check_hostname(req, arg->mon_name)) == 0) {
res.res_stat = stat_fail;
}
if (err != 0) {
DEBUG(1, "stat called for host %s", arg->mon_name);
if (getaddrinfo(arg->mon_name, NULL, NULL, &ai) == 0) {
res.res_stat = stat_succ;
freeaddrinfo(ai);
} else {
log(LOG_ERR, "invalid hostname to sm_stat from %s: %s",
addrstr(svc_getcaller_sa(req->rq_xprt)), arg->mon_name);
res.res_stat = stat_fail;
}
}
res.state = ntohl(status_info->fh_state);
return (&res);
}
struct sm_stat_res *
sm_mon_1_svc(mon * arg, struct svc_req * req)
{
static sm_stat_res res;
MonitoredHost *mhp;
Notify *np;
struct addrinfo *ai;
int namelen;
res.res_stat = stat_fail;
if (sm_check_hostname(req, arg->mon_id.mon_name) == 0)
return (&res);
DEBUG(1, "monitor request for host %s", arg->mon_id.mon_name);
DEBUG(1, "recall host: %s prog: %d ver: %d proc: %d",
arg->mon_id.my_id.my_name, arg->mon_id.my_id.my_prog,
arg->mon_id.my_id.my_vers, arg->mon_id.my_id.my_proc);
res.state = ntohl(status_info->fh_state);
if (getaddrinfo(arg->mon_id.mon_name, NULL, NULL, &ai) != 0) {
log(LOG_ERR, "Invalid hostname to sm_mon: %s", arg->mon_id.mon_name);
return (&res);
}
freeaddrinfo(ai);
namelen = strlen(arg->mon_id.my_id.my_name);
np = malloc(sizeof(Notify) + namelen);
if (!np) {
log(LOG_ERR, "Out of memory");
return (&res);
}
mhp = find_host(arg->mon_id.mon_name, TRUE);
if (!mhp) {
free(np);
return (&res);
}
strncpy(np->n_host, arg->mon_id.my_id.my_name, namelen+1);
np->n_prog = arg->mon_id.my_id.my_prog;
np->n_vers = arg->mon_id.my_id.my_vers;
np->n_proc = arg->mon_id.my_id.my_proc;
memcpy(np->n_data, arg->priv, sizeof(np->n_data));
np->n_next = mhp->mh_notify_list;
mhp->mh_notify_list = np;
res.res_stat = stat_succ;
return (&res);
}
static int
do_unmon(MonitoredHost * mhp, my_id * idp)
{
Notify *np, *next, *last = NULL;
int result = FALSE;
np = mhp->mh_notify_list;
if (!np)
return (result);
while (np) {
if (!strncasecmp(idp->my_name, np->n_host, SM_MAXSTRLEN)
&& (idp->my_prog == np->n_prog) && (idp->my_proc == np->n_proc)
&& (idp->my_vers == np->n_vers)) {
next = np->n_next;
if (last)
last->n_next = next;
else
mhp->mh_notify_list = next;
free(np);
np = next;
result = TRUE;
} else {
last = np;
np = np->n_next;
}
}
if (result && !mhp->mh_notify_list) {
HostInfo *hip = HOSTINFO(mhp->mh_hostinfo_offset);
hip->hi_monitored = 0;
sync_file();
tdelete(mhp, &mhroot, mhcmp);
free(mhp);
}
return (result);
}
struct sm_stat *
sm_unmon_1_svc(mon_id * arg, struct svc_req * req __unused)
{
static sm_stat res;
MonitoredHost *mhp;
DEBUG(1, "un-monitor request for host %s", arg->mon_name);
DEBUG(1, "recall host: %s prog: %d ver: %d proc: %d", arg->my_id.my_name,
arg->my_id.my_prog, arg->my_id.my_vers, arg->my_id.my_proc);
if ((mhp = find_host(arg->mon_name, FALSE))) {
if (!do_unmon(mhp, &arg->my_id))
log(LOG_ERR, "unmon request from %s, no matching monitor",
arg->my_id.my_name);
} else {
log(LOG_ERR, "unmon request from %s for unknown host %s",
arg->my_id.my_name, arg->mon_name);
}
res.state = ntohl(status_info->fh_state);
return (&res);
}
struct sm_stat *
sm_unmon_all_1_svc(my_id * arg, struct svc_req * req __unused)
{
static sm_stat res;
HostInfo *hip;
MonitoredHost mhtmp, **mhpp;
off_t off;
uint i;
DEBUG(1, "unmon_all for host: %s prog: %d ver: %d proc: %d",
arg->my_name, arg->my_prog, arg->my_vers, arg->my_proc);
bzero(&mhtmp, sizeof(mhtmp));
off = sizeof(FileHeader);
for (i = 0; i < ntohl(status_info->fh_reccnt); i++) {
hip = HOSTINFO(off);
off += ntohs(hip->hi_len);
if (hip->hi_monitored) {
mhtmp.mh_name = hip->hi_name;
mhpp = tfind(&mhtmp, &mhroot, mhcmp);
if (mhpp)
do_unmon(*mhpp, arg);
}
}
res.state = ntohl(status_info->fh_state);
return (&res);
}
void *
sm_simu_crash_1_svc(void *v __unused, struct svc_req * req)
{
static char dummy;
int need_notify = FALSE;
HostInfo *hip;
off_t off;
uint i;
if (!config.simu_crash_allowed) {
struct sockaddr *claddr;
char hname[NI_MAXHOST];
claddr = svc_getcaller_sa(req->rq_xprt);
if (!claddr || getnameinfo(claddr, claddr->sa_len, hname, sizeof(hname), NULL, 0, 0))
strlcpy(hname, "<unknown>", sizeof(hname));
log(LOG_WARNING, "simu_crash call from %s denied!", hname);
return (&dummy);
}
log(LOG_WARNING, "simu_crash called!!");
off = sizeof(FileHeader);
for (i = 0; i < ntohl(status_info->fh_reccnt); i++) {
hip = HOSTINFO(off);
if (hip->hi_monitored)
hip->hi_notify = htons(1);
if (hip->hi_notify)
need_notify = TRUE;
off += ntohs(hip->hi_len);
}
status_info->fh_state = htonl(ntohl(status_info->fh_state) + 2);
if (need_notify && !get_statd_notify_pid()) {
log(LOG_INFO, "need to start statd notify");
if (statd_notify_is_loaded())
statd_notify_start();
else
statd_notify_load();
}
return (&dummy);
}
void *
sm_notify_1_svc(stat_chge * arg, struct svc_req * req)
{
struct timeval timeout = {20, 0};
struct timeval try = {4, 0};
CLIENT *cli;
static char dummy;
char empty[] = "", *spcerr;
sm_status tx_arg;
MonitoredHost *mhp;
Notify *np;
HostInfo *hip;
pid_t pid;
struct addrinfo *ai, *ailist;
int error, sock, result;
DEBUG(1, "notify from host %s, new state %d", arg->mon_name, arg->state);
mhp = find_host(arg->mon_name, FALSE);
if (!mhp) {
struct sockaddr *claddr;
if ((claddr = svc_getcaller_sa(req->rq_xprt))) {
void *sinaddr;
socklen_t sinlen;
struct hostent *he;
if (claddr->sa_family == AF_INET) {
sinaddr = &((struct sockaddr_in*)claddr)->sin_addr;
sinlen = sizeof(((struct sockaddr_in*)claddr)->sin_addr);
} else {
sinaddr = &((struct sockaddr_in6*)claddr)->sin6_addr;
sinlen = sizeof(((struct sockaddr_in6*)claddr)->sin6_addr);
}
he = gethostbyaddr(sinaddr, sinlen, claddr->sa_family);
if (he) {
char **npp = he->h_aliases;
if (strlen(he->h_name) <= SM_MAXSTRLEN)
mhp = find_host(he->h_name, FALSE);
while (!mhp && *npp) {
if (strlen(*npp) <= SM_MAXSTRLEN)
mhp = find_host(*npp, FALSE);
if (!mhp)
npp++;
}
}
if (!mhp)
mhp = find_host(addrstr(claddr), FALSE);
if (mhp)
DEBUG(1, "Notification from host %s found as %s",
arg->mon_name, HOSTINFO(mhp->mh_hostinfo_offset)->hi_name);
}
if (!mhp) {
DEBUG(1, "Unsolicited notification from host %s", arg->mon_name);
return (&dummy);
}
}
hip = HOSTINFO(mhp->mh_hostinfo_offset);
np = mhp->mh_notify_list;
if (!np)
return (&dummy);
pid = fork();
if (pid == -1) {
log(LOG_ERR, "Unable to fork notify process - %s", strerror(errno));
return (NULL);
}
if (pid)
return (&dummy);
for (; np; np = np->n_next) {
tx_arg.mon_name = hip->hi_name;
tx_arg.state = arg->state;
memcpy(tx_arg.priv, np->n_data, sizeof(tx_arg.priv));
result = FALSE;
spcerr = NULL;
ailist = NULL;
if ((error = getaddresslist(np->n_host, &ailist))) {
log(LOG_ERR, "Failed to contact host %s - error %d resolving", np->n_host, error);
continue;
}
if (!ailist) {
log(LOG_ERR, "Failed to contact host %s - no addresses", np->n_host);
continue;
}
for (ai=ailist; ai && (result == FALSE); ai = ai->ai_next) {
if (config.send_using_tcp) {
sock = RPC_ANYSOCK;
cli = clnttcp_create_sa(ai->ai_addr, np->n_prog, np->n_vers, &sock, 0, 0);
}
if (!config.send_using_tcp || !cli)
cli = clntudp_create_sa(ai->ai_addr, np->n_prog, np->n_vers, try, &sock);
if (!cli) {
spcerr = clnt_spcreateerror(empty);
continue;
}
if (clnt_call(cli, np->n_proc, (xdrproc_t)xdr_sm_status, &tx_arg,
(xdrproc_t) xdr_void, &dummy, timeout) == RPC_SUCCESS)
result = TRUE;
clnt_destroy(cli);
}
if (result == FALSE) {
if (spcerr)
log(LOG_WARNING, "Failed to contact rpc.statd client host %s%s", np->n_host, spcerr);
log(LOG_ERR, "Failed to call rpc.statd client at host %s", np->n_host);
}
freeaddrinfo(ailist);
}
exit(0);
}