#include "nsm.h"
#include "nfsrpc.h"
-#ifndef HAVE_DECL_AI_ADDRCONFIG
-#define AI_ADDRCONFIG 0
+/* glibc before 2.3.4 */
+#ifndef AI_NUMERICSERV
+#define AI_NUMERICSERV 0
#endif
#define NSM_TIMEOUT 2
struct nsm_host {
struct nsm_host * next;
char * name;
- char * mon_name;
- char * my_name;
+ const char * mon_name;
+ const char * my_name;
+ char * notify_arg;
struct addrinfo *ai;
time_t last_used;
time_t send_next;
uint32_t xid;
};
-static char nsm_hostname[256];
+static char nsm_hostname[SM_MAXSTRLEN + 1];
static int nsm_state;
static int nsm_family = AF_INET;
static int opt_debug = 0;
{
struct addrinfo *ai = NULL;
struct addrinfo hint = {
- .ai_flags = AI_ADDRCONFIG,
.ai_family = (nsm_family == AF_INET ? AF_INET: AF_UNSPEC),
.ai_protocol = (int)IPPROTO_UDP,
};
return ai;
}
+#ifdef HAVE_GETNAMEINFO
+static char *
+smn_get_hostname(const struct sockaddr *sap, const socklen_t salen,
+ const char *name)
+{
+ char buf[NI_MAXHOST];
+ int error;
+
+ error = getnameinfo(sap, salen, buf, sizeof(buf), NULL, 0, NI_NAMEREQD);
+ if (error != 0) {
+ xlog(L_ERROR, "my_name '%s' is unusable: %s",
+ name, gai_strerror(error));
+ return NULL;
+ }
+ return strdup(buf);
+}
+#else /* !HAVE_GETNAMEINFO */
+static char *
+smn_get_hostname(const struct sockaddr *sap,
+ __attribute__ ((unused)) const socklen_t salen,
+ const char *name)
+{
+ const struct sockaddr_in *sin = (const struct sockaddr_in *)(char *)sap;
+ const struct in_addr *addr = &sin->sin_addr;
+ struct hostent *hp;
+
+ if (sap->sa_family != AF_INET) {
+ xlog(L_ERROR, "my_name '%s' is unusable: Bad address family",
+ name);
+ return NULL;
+ }
+
+ hp = gethostbyaddr(addr, (socklen_t)sizeof(addr), AF_INET);
+ if (hp == NULL) {
+ xlog(L_ERROR, "my_name '%s' is unusable: %s",
+ name, hstrerror(h_errno));
+ return NULL;
+ }
+ return strdup(hp->h_name);
+}
+#endif /* !HAVE_GETNAMEINFO */
+
+/*
+ * Presentation addresses are converted to their canonical hostnames.
+ * If the IP address does not map to a hostname, it is an error:
+ * we never send a presentation address as the argument of SM_NOTIFY.
+ *
+ * If "name" is not a presentation address, it is left alone. This
+ * allows the administrator some flexibility if DNS isn't configured
+ * exactly how sm-notify prefers it.
+ *
+ * Returns NUL-terminated C string containing the result, or NULL
+ * if the canonical name doesn't exist or cannot be determined.
+ * The caller must free the result with free(3).
+ */
+__attribute_malloc__
+static char *
+smn_verify_my_name(const char *name)
+{
+ struct addrinfo *ai = NULL;
+ struct addrinfo hint = {
+#ifdef IPV6_SUPPORTED
+ .ai_family = AF_UNSPEC,
+#else /* !IPV6_SUPPORTED */
+ .ai_family = AF_INET,
+#endif /* !IPV6_SUPPORTED */
+ .ai_flags = AI_NUMERICHOST,
+ };
+ char *retval;
+ int error;
+
+ error = getaddrinfo(name, NULL, &hint, &ai);
+ switch (error) {
+ case 0:
+ /* @name was a presentation address */
+ retval = smn_get_hostname(ai->ai_addr, ai->ai_addrlen, name);
+ freeaddrinfo(ai);
+ if (retval == NULL)
+ return NULL;
+ break;
+ case EAI_NONAME:
+ /* @name was not a presentation address */
+ retval = strdup(name);
+ break;
+ default:
+ xlog(L_ERROR, "my_name '%s' is unusable: %s",
+ name, gai_strerror(error));
+ return NULL;
+ }
+
+ xlog(D_GENERAL, "Canonical name for my_name '%s': %s",
+ name, retval);
+ return retval;
+}
+
__attribute_malloc__
static struct nsm_host *
smn_alloc_host(const char *hostname, const char *mon_name,
if (host == NULL)
goto out_nomem;
+ /*
+ * mon_name and my_name are preserved so sm-notify can
+ * find the right monitor record to remove when it is
+ * done processing this host.
+ */
host->name = strdup(hostname);
- host->mon_name = strdup(mon_name);
- host->my_name = strdup(my_name);
+ host->mon_name = (const char *)strdup(mon_name);
+ host->my_name = (const char *)strdup(my_name);
+ host->notify_arg = strdup(opt_srcaddr != NULL ?
+ nsm_hostname : my_name);
if (host->name == NULL ||
host->mon_name == NULL ||
- host->my_name == NULL) {
- free(host->my_name);
- free(host->mon_name);
+ host->my_name == NULL ||
+ host->notify_arg == NULL) {
+ free(host->notify_arg);
+ free((void *)host->my_name);
+ free((void *)host->mon_name);
free(host->name);
free(host);
goto out_nomem;
static void smn_forget_host(struct nsm_host *host)
{
- xlog(D_CALL, "Removing %s from notify list", host->name);
+ xlog(D_CALL, "Removing %s (%s, %s) from notify list",
+ host->name, host->mon_name, host->my_name);
- nsm_delete_notified_host(host->name);
+ nsm_delete_notified_host(host->name, host->mon_name, host->my_name);
- free(host->my_name);
- free(host->mon_name);
+ free(host->notify_arg);
+ free((void *)host->my_name);
+ free((void *)host->mon_name);
free(host->name);
if (host->ai)
freeaddrinfo(host->ai);
return 0;
insert_host(host);
- xlog(D_GENERAL, "Added host %s to notify list", hostname);
return 1;
}
if (srcaddr == NULL)
hint.ai_flags |= AI_PASSIVE;
+ /* Do not allow "node" and "service" parameters both to be NULL */
if (srcport == NULL)
error = getaddrinfo(srcaddr, "", &hint, &ai);
else
exit(1);
}
- xlog_syslog(1);
if (opt_debug) {
+ xlog_syslog(0);
xlog_stderr(1);
xlog_config(D_ALL, 1);
- } else
+ } else {
+ xlog_syslog(1);
xlog_stderr(0);
+ }
xlog_open(progname);
xlog(L_NOTICE, "Version " VERSION " starting");
}
}
- if (opt_srcaddr) {
- strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)-1);
- } else
- if (gethostname(nsm_hostname, sizeof(nsm_hostname)) < 0) {
- xlog(L_ERROR, "Failed to obtain name of local host: %m");
- exit(1);
+ if (opt_srcaddr != NULL) {
+ char *name;
+
+ name = smn_verify_my_name(opt_srcaddr);
+ if (name == NULL)
+ exit(1);
+
+ strncpy(nsm_hostname, name, sizeof(nsm_hostname));
+ free(name);
}
(void)nsm_retire_monitored_hosts();
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);
-
+ SM_PROG, host->notify_arg, nsm_state);
+
return 0;
}
+static void
+smn_defer(struct nsm_host *host)
+{
+ host->xid = 0;
+ host->send_next = time(NULL) + NSM_MAX_TIMEOUT;
+ host->timeout = NSM_MAX_TIMEOUT;
+ insert_host(host);
+}
+
+static void
+smn_schedule(struct nsm_host *host)
+{
+ host->retries = 0;
+ host->xid = 0;
+ host->send_next = time(NULL);
+ host->timeout = NSM_TIMEOUT;
+ insert_host(host);
+}
+
/*
* Extract the returned port number and set up the SM_NOTIFY call.
*/
{
uint16_t port = nsm_recv_rpcbind(sap->sa_family, xdr);
- host->send_next = time(NULL);
- host->xid = 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;
+ smn_defer(host);
} else {
+ xlog(D_GENERAL, "Processing rpcbind reply for %s (port %u)",
+ host->name, port);
nfs_set_port(sap, port);
- if (host->timeout >= NSM_MAX_TIMEOUT / 4)
- host->timeout = NSM_MAX_TIMEOUT / 4;
+ smn_schedule(host);
}
-
- insert_host(host);
}
/*
- * Successful NOTIFY call. Server returns void, so nothing
- * we need to do here.
+ * Successful NOTIFY call. Server returns void.
+ *
+ * Try sending another SM_NOTIFY with an unqualified "my_name"
+ * argument. Reuse the port number. If "my_name" is already
+ * unqualified, we're done.
*/
static void
recv_notify_reply(struct nsm_host *host)
{
- xlog(D_GENERAL, "Host %s notified successfully", host->name);
+ char *dot = strchr(host->notify_arg, '.');
- smn_forget_host(host);
+ if (dot != NULL) {
+ *dot = '\0';
+ smn_schedule(host);
+ } else {
+ xlog(D_GENERAL, "Host %s notified successfully", host->name);
+ smn_forget_host(host);
+ }
}
/*
}
/*
- * Insert host into sorted list
+ * Insert host into notification list, sorted by next send time
*/
static void
insert_host(struct nsm_host *host)
host->next = *where;
*where = host;
+ xlog(D_GENERAL, "Added host %s to notify list", host->name);
}
/*