#include <errno.h>
#include <grp.h>
+#include "sockaddr.h"
#include "xlog.h"
#include "nsm.h"
#include "nfsrpc.h"
+/* glibc before 2.3.4 */
+#ifndef AI_NUMERICSERV
+#define AI_NUMERICSERV 0
+#endif
+
#define NSM_TIMEOUT 2
#define NSM_MAX_TIMEOUT 120 /* don't make this too big */
struct nsm_host {
struct nsm_host * next;
char * name;
+ char * mon_name;
+ char * my_name;
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;
static _Bool opt_update_state = true;
static unsigned int opt_max_retry = 15 * 60;
-static char * opt_srcaddr = 0;
-static uint16_t opt_srcport = 0;
+static char * opt_srcaddr = NULL;
+static char * opt_srcport = NULL;
static void notify(const int sock);
static int notify_host(int, struct nsm_host *);
static struct nsm_host * hosts = NULL;
-static struct addrinfo *smn_lookup(const char *name)
+__attribute_malloc__
+static struct addrinfo *
+smn_lookup(const char *name)
{
- struct addrinfo *ai, hint = {
-#if HAVE_DECL_AI_ADDRCONFIG
- .ai_flags = AI_ADDRCONFIG,
-#endif /* HAVE_DECL_AI_ADDRCONFIG */
- .ai_family = AF_INET,
- .ai_protocol = IPPROTO_UDP,
+ struct addrinfo *ai = NULL;
+ struct addrinfo hint = {
+ .ai_family = (nsm_family == AF_INET ? AF_INET: AF_UNSPEC),
+ .ai_protocol = (int)IPPROTO_UDP,
};
int error;
error = getaddrinfo(name, NULL, &hint, &ai);
- if (error) {
+ if (error != 0) {
xlog(D_GENERAL, "getaddrinfo(3): %s", gai_strerror(error));
return NULL;
}
__attribute_malloc__
static struct nsm_host *
-smn_alloc_host(const char *hostname, const time_t timestamp)
+smn_alloc_host(const char *hostname, const char *mon_name,
+ const char *my_name, const time_t timestamp)
{
struct nsm_host *host;
goto out_nomem;
host->name = strdup(hostname);
- if (host->name == NULL) {
+ host->mon_name = strdup(mon_name);
+ host->my_name = strdup(my_name);
+ if (host->name == NULL ||
+ host->mon_name == NULL ||
+ host->my_name == NULL) {
+ free(host->my_name);
+ free(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->name);
if (host->ai)
freeaddrinfo(host->ai);
static unsigned int
smn_get_host(const char *hostname,
__attribute__ ((unused)) const struct sockaddr *sap,
- __attribute__ ((unused)) const struct mon *m,
- const time_t timestamp)
+ const struct mon *m, const time_t timestamp)
{
struct nsm_host *host;
- host = smn_alloc_host(hostname, timestamp);
+ host = smn_alloc_host(hostname,
+ m->mon_id.mon_name, m->mon_id.my_id.my_name, timestamp);
if (host == NULL)
return 0;
}
#endif /* !IPV6_SUPPORTED */
+/*
+ * If admin specified a source address or srcport, then convert those
+ * to a sockaddr and return it. Otherwise, return an ANYADDR address.
+ */
+__attribute_malloc__
+static struct addrinfo *
+smn_bind_address(const char *srcaddr, const char *srcport)
+{
+ struct addrinfo *ai = NULL;
+ struct addrinfo hint = {
+ .ai_flags = AI_NUMERICSERV,
+ .ai_family = nsm_family,
+ .ai_protocol = (int)IPPROTO_UDP,
+ };
+ int error;
+
+ 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
+ error = getaddrinfo(srcaddr, srcport, &hint, &ai);
+ if (error != 0) {
+ xlog(L_ERROR,
+ "Invalid bind address or port for RPC socket: %s",
+ gai_strerror(error));
+ return NULL;
+ }
+
+ return ai;
+}
+
#ifdef HAVE_LIBTIRPC
static int
smn_bindresvport(int sock, struct sockaddr *sap)
* an error occurs.
*/
static int
-smn_create_socket(const char *srcaddr, const uint16_t srcport)
+smn_create_socket(const char *srcaddr, const char *srcport)
{
- struct sockaddr_storage address;
- struct sockaddr *local_addr = (struct sockaddr *)&address;
int sock, retry_cnt = 0;
+ struct addrinfo *ai;
retry:
sock = smn_socket();
if (sock == -1)
return -1;
- memset(&address, 0, sizeof(address));
- local_addr->sa_family = AF_INET; /* Default to IPv4 */
-
- /* Bind source IP if provided on command line */
- if (srcaddr) {
- struct addrinfo *ai = smn_lookup(srcaddr);
- if (!ai) {
- xlog(L_ERROR,
- "Not a valid hostname or address: \"%s\"",
- srcaddr);
- (void)close(sock);
- return -1;
- }
-
- /* We know it's IPv4 at this point */
- memcpy(local_addr, ai->ai_addr, ai->ai_addrlen);
-
- freeaddrinfo(ai);
+ ai = smn_bind_address(srcaddr, srcport);
+ if (ai == NULL) {
+ (void)close(sock);
+ return -1;
}
/* Use source port if provided on the command line,
* otherwise use bindresvport */
if (srcport) {
- nfs_set_port(local_addr, srcport);
- if (bind(sock, local_addr, sizeof(struct sockaddr_in)) < 0) {
+ if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
xlog(L_ERROR, "Failed to bind RPC socket: %m");
+ freeaddrinfo(ai);
(void)close(sock);
return -1;
}
} else {
struct servent *se;
- struct sockaddr_in *sin = (struct sockaddr_in *)local_addr;
- if (smn_bindresvport(sock, local_addr) == -1) {
+ if (smn_bindresvport(sock, ai->ai_addr) == -1) {
xlog(L_ERROR,
"bindresvport on RPC socket failed: %m");
+ freeaddrinfo(ai);
(void)close(sock);
return -1;
}
/* try to avoid known ports */
- se = getservbyport(sin->sin_port, "udp");
- if (se && retry_cnt < 100) {
+ se = getservbyport((int)nfs_get_port(ai->ai_addr), "udp");
+ if (se != NULL && retry_cnt < 100) {
retry_cnt++;
- close(sock);
+ freeaddrinfo(ai);
+ (void)close(sock);
goto retry;
}
}
+ freeaddrinfo(ai);
return sock;
}
opt_update_state = false;
break;
case 'p':
- opt_srcport = atoi(optarg);
+ opt_srcport = optarg;
break;
case 'v':
opt_srcaddr = optarg;
}
}
- 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) {
+ struct addrinfo *ai = NULL;
+ struct addrinfo hint = {
+ .ai_family = AF_UNSPEC,
+ .ai_flags = AI_NUMERICHOST,
+ };
+
+ if (getaddrinfo(opt_srcaddr, NULL, &hint, &ai))
+ /* not a presentation address - use it */
+ strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname));
+ else {
+ /* was a presentation address - look it up in
+ * /etc/hosts, so it can be used for my_name */
+ int error;
+
+ freeaddrinfo(ai);
+ hint.ai_flags = AI_CANONNAME;
+ error = getaddrinfo(opt_srcaddr, NULL, &hint, &ai);
+ if (error != 0) {
+ xlog(L_ERROR, "Bind address %s is unusable: %s",
+ opt_srcaddr, gai_strerror(error));
+ exit(1);
+ }
+ strncpy(nsm_hostname, ai->ai_canonname,
+ sizeof(nsm_hostname));
+ freeaddrinfo(ai);
+ }
}
(void)nsm_retire_monitored_hosts();
static int
notify_host(int sock, struct nsm_host *host)
{
+ const char *my_name = (opt_srcaddr != NULL ?
+ nsm_hostname : host->my_name);
struct sockaddr *sap;
socklen_t salen;
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, my_name, nsm_state);
+
return 0;
}
}
/*
- * 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->my_name, '.');
- smn_forget_host(host);
+ if (dot != NULL) {
+ *dot = '\0';
+ host->send_next = time(NULL);
+ host->xid = 0;
+ if (host->timeout >= NSM_MAX_TIMEOUT / 4)
+ host->timeout = NSM_MAX_TIMEOUT / 4;
+ insert_host(host);
+ } else {
+ xlog(D_GENERAL, "Host %s notified successfully", host->name);
+ smn_forget_host(host);
+ }
}
/*