#include "nsm.h"
#include "nfsrpc.h"
-#define NSM_PROG 100024
-#define NSM_PROGRAM 100024
-#define NSM_VERSION 1
#define NSM_TIMEOUT 2
-#define NSM_NOTIFY 6
#define NSM_MAX_TIMEOUT 120 /* don't make this too big */
-#define MAXMSGSIZE 256
struct nsm_host {
struct nsm_host * next;
char * name;
- struct sockaddr_storage addr;
struct addrinfo *ai;
time_t last_used;
time_t send_next;
unsigned int timeout;
unsigned int retries;
- unsigned int xid;
+ uint32_t xid;
};
static char nsm_hostname[256];
static int
notify_host(int sock, struct nsm_host *host)
{
- struct sockaddr_storage address;
- struct sockaddr *dest = (struct sockaddr *)&address;
- socklen_t destlen = sizeof(address);
- static unsigned int xid = 0;
- uint32_t msgbuf[MAXMSGSIZE], *p;
- unsigned int len;
-
- if (!xid)
- xid = getpid() + time(NULL);
- if (!host->xid)
- host->xid = xid++;
+ struct sockaddr *sap;
+ socklen_t salen;
if (host->ai == NULL) {
host->ai = smn_lookup(host->name);
}
}
- memset(msgbuf, 0, sizeof(msgbuf));
- p = msgbuf;
- *p++ = htonl(host->xid);
- *p++ = 0;
- *p++ = htonl(2);
-
/* If we retransmitted 4 times, reset the port to force
* a new portmap lookup (in case statd was restarted).
* We also rotate through multiple IP addresses at this
*/
if (host->retries >= 4) {
/* don't rotate if there is only one addrinfo */
- if (host->ai->ai_next == NULL)
- memcpy(&host->addr, host->ai->ai_addr,
- host->ai->ai_addrlen);
- else {
+ if (host->ai->ai_next != NULL) {
struct addrinfo *first = host->ai;
struct addrinfo **next = &host->ai;
next = & (*next)->ai_next;
/* put first entry at end */
*next = first;
- memcpy(&host->addr, first->ai_addr,
- first->ai_addrlen);
}
- nfs_set_port((struct sockaddr *)&host->addr, 0);
+ nfs_set_port(host->ai->ai_addr, 0);
host->retries = 0;
}
- memcpy(dest, &host->addr, destlen);
- if (nfs_get_port(dest) == 0) {
- /* Build a PMAP packet */
- xlog(D_GENERAL, "Sending portmap query to %s", host->name);
+ sap = host->ai->ai_addr;
+ salen = host->ai->ai_addrlen;
- nfs_set_port(dest, 111);
- *p++ = htonl(100000);
- *p++ = htonl(2);
- *p++ = htonl(3);
+ if (nfs_get_port(sap) == 0)
+ host->xid = nsm_xmit_rpcbind(sock, sap, SM_PROG, SM_VERS);
+ else
+ host->xid = nsm_xmit_notify(sock, sap, salen,
+ SM_PROG, nsm_hostname, nsm_state);
+
+ return 0;
+}
- /* Auth and verf */
- *p++ = 0; *p++ = 0;
- *p++ = 0; *p++ = 0;
+/*
+ * Extract the returned port number and set up the SM_NOTIFY call.
+ */
+static void
+recv_rpcbind_reply(struct sockaddr *sap, struct nsm_host *host, XDR *xdr)
+{
+ uint16_t port = nsm_recv_rpcbind(sap->sa_family, xdr);
+
+ host->send_next = time(NULL);
+ host->xid = 0;
- *p++ = htonl(NSM_PROGRAM);
- *p++ = htonl(NSM_VERSION);
- *p++ = htonl(IPPROTO_UDP);
- *p++ = 0;
+ if (port == 0) {
+ /* No binding for statd... */
+ xlog(D_GENERAL, "No statd on host %s", host->name);
+ host->timeout = NSM_MAX_TIMEOUT;
+ host->send_next += NSM_MAX_TIMEOUT;
} else {
- /* Build an SM_NOTIFY packet */
- xlog(D_GENERAL, "Sending SM_NOTIFY to %s", host->name);
-
- *p++ = htonl(NSM_PROGRAM);
- *p++ = htonl(NSM_VERSION);
- *p++ = htonl(NSM_NOTIFY);
-
- /* Auth and verf */
- *p++ = 0; *p++ = 0;
- *p++ = 0; *p++ = 0;
-
- /* state change */
- len = strlen(nsm_hostname);
- *p++ = htonl(len);
- memcpy(p, nsm_hostname, len);
- p += (len + 3) >> 2;
- *p++ = htonl(nsm_state);
+ nfs_set_port(sap, port);
+ if (host->timeout >= NSM_MAX_TIMEOUT / 4)
+ host->timeout = NSM_MAX_TIMEOUT / 4;
}
- len = (p - msgbuf) << 2;
- if (sendto(sock, msgbuf, len, 0, dest, destlen) < 0)
- xlog_warn("Sending Reboot Notification to "
- "'%s' failed: errno %d (%m)", host->name, errno);
-
- return 0;
+ insert_host(host);
+}
+
+/*
+ * Successful NOTIFY call. Server returns void, so nothing
+ * we need to do here.
+ */
+static void
+recv_notify_reply(struct nsm_host *host)
+{
+ xlog(D_GENERAL, "Host %s notified successfully", host->name);
+
+ smn_forget_host(host);
}
/*
{
struct nsm_host *hp;
struct sockaddr *sap;
- uint32_t msgbuf[MAXMSGSIZE], *p, *end;
+ char msgbuf[NSM_MAXMSGSIZE];
uint32_t xid;
- int res;
+ ssize_t msglen;
+ XDR xdr;
- res = recv(sock, msgbuf, sizeof(msgbuf), 0);
- if (res < 0)
+ memset(msgbuf, 0 , sizeof(msgbuf));
+ msglen = recv(sock, msgbuf, sizeof(msgbuf), 0);
+ if (msglen < 0)
return;
xlog(D_GENERAL, "Received packet...");
- p = msgbuf;
- end = p + (res >> 2);
-
- xid = ntohl(*p++);
- if (*p++ != htonl(1) /* must be REPLY */
- || *p++ != htonl(0) /* must be ACCEPTED */
- || *p++ != htonl(0) /* must be NULL verifier */
- || *p++ != htonl(0)
- || *p++ != htonl(0)) /* must be SUCCESS */
- return;
+ memset(&xdr, 0, sizeof(xdr));
+ xdrmem_create(&xdr, msgbuf, (unsigned int)msglen, XDR_DECODE);
+ xid = nsm_parse_reply(&xdr);
+ if (xid == 0)
+ goto out;
/* Before we look at the data, find the host struct for
this reply */
if ((hp = find_host(xid)) == NULL)
- return;
- sap = (struct sockaddr *)&hp->addr;
-
- if (nfs_get_port(sap) == 0) {
- /* This was a portmap request */
- unsigned int port;
-
- port = ntohl(*p++);
- if (p > end)
- goto fail;
-
- hp->send_next = time(NULL);
- if (port == 0) {
- /* No binding for statd. Delay the next
- * portmap query for max timeout */
- xlog(D_GENERAL, "No statd on %s", hp->name);
- hp->timeout = NSM_MAX_TIMEOUT;
- hp->send_next += NSM_MAX_TIMEOUT;
- } else {
- nfs_set_port(sap, port);
- if (hp->timeout >= NSM_MAX_TIMEOUT / 4)
- hp->timeout = NSM_MAX_TIMEOUT / 4;
- }
- hp->xid = 0;
- } else {
- /* Successful NOTIFY call. Server returns void,
- * so nothing we need to do here (except
- * check that we didn't read past the end of the
- * packet)
- */
- if (p <= end) {
- xlog(D_GENERAL, "Host %s notified successfully",
- hp->name);
- smn_forget_host(hp);
- return;
- }
- }
+ goto out;
+
+ sap = hp->ai->ai_addr;
+ if (nfs_get_port(sap) == 0)
+ recv_rpcbind_reply(sap, hp, &xdr);
+ else
+ recv_notify_reply(hp);
-fail: /* Re-insert the host */
- insert_host(hp);
+out:
+ xdr_destroy(&xdr);
}
/*