aboutsummaryrefslogtreecommitdiffstats
path: root/erts/epmd/src
diff options
context:
space:
mode:
Diffstat (limited to 'erts/epmd/src')
-rw-r--r--erts/epmd/src/Makefile.in29
-rw-r--r--erts/epmd/src/epmd.c94
-rw-r--r--erts/epmd/src/epmd.h21
-rw-r--r--erts/epmd/src/epmd_cli.c10
-rw-r--r--erts/epmd/src/epmd_int.h68
-rw-r--r--erts/epmd/src/epmd_srv.c436
6 files changed, 371 insertions, 287 deletions
diff --git a/erts/epmd/src/Makefile.in b/erts/epmd/src/Makefile.in
index 498756b468..2f5296a5e8 100644
--- a/erts/epmd/src/Makefile.in
+++ b/erts/epmd/src/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1998-2009. All Rights Reserved.
+# Copyright Ericsson AB 1998-2010. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -49,15 +49,31 @@ include ../epmd.mk
BINDIR = $(ERL_TOP)/bin/$(TARGET)
OBJDIR = $(ERL_TOP)/erts/obj$(TYPEMARKER)/$(TARGET)
+ERTS_INCL = -I$(ERL_TOP)/erts/include \
+ -I$(ERL_TOP)/erts/include/$(TARGET) \
+ -I$(ERL_TOP)/erts/include/internal \
+ -I$(ERL_TOP)/erts/include/internal/$(TARGET)
+
+# On windows we always need reentrant libraries.
+ifeq ($(TARGET),win32)
+ERTS_INTERNAL_LIBS=-L../../lib/internal/$(TARGET) -lerts_internal_r$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@
+else
+ifeq ($(findstring vxworks,$(TARGET)),vxworks)
+ERTS_INTERNAL_LIBS=-L../../lib/internal/$(TARGET) -lerts_internal$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@
+else
+ERTS_INTERNAL_LIBS=-L../../lib/internal/$(TARGET) -lerts_internal$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@ -lm
+endif
+endif
CC = @CC@
WFLAGS = @WFLAGS@
-CFLAGS = @CFLAGS@ @DEFS@ $(TYPE_FLAGS) $(WFLAGS)
+CFLAGS = @CFLAGS@ @DEFS@ $(TYPE_FLAGS) $(WFLAGS) $(ERTS_INCL)
LD = @LD@
-LIBS = @LIBS@
+LIBS = @LIBS@ $(ERTS_INTERNAL_LIBS)
LDFLAGS = @LDFLAGS@
+
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -90,7 +106,7 @@ EPMD_OBJS = $(OBJDIR)/epmd.o \
#---------------------------------
-all: $(BINDIR)/$(EPMD)
+all: erts_lib $(BINDIR)/$(EPMD)
docs:
@@ -109,9 +125,12 @@ clean:
$(BINDIR)/$(EPMD): $(EPMD_OBJS)
$(PURIFY) $(LD) $(LDFLAGS) -o $@ $(EPMD_OBJS) $(LIBS)
-$(OBJDIR)/%.o: %.c
+$(OBJDIR)/%.o: %.c epmd.h epmd_int.h
$(CC) $(CFLAGS) $(EPMD_FLAGS) -o $@ -c $<
+erts_lib:
+ cd $(ERL_TOP)/erts/lib_src && $(MAKE) $(TYPE)
+
include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: all
diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c
index 9c2ce065bb..a1f202251c 100644
--- a/erts/epmd/src/epmd.c
+++ b/erts/epmd/src/epmd.c
@@ -23,6 +23,7 @@
#endif
#include "epmd.h" /* Renamed from 'epmd_r4.h' */
#include "epmd_int.h"
+#include "erl_printf.h"
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
@@ -32,7 +33,9 @@
static void usage(EpmdVars *);
static void run_daemon(EpmdVars*);
+static char* get_addresses(void);
static int get_port_no(void);
+static int check_relaxed(void);
#ifdef __WIN32__
static int has_console(void);
#endif
@@ -131,6 +134,7 @@ int main(int argc, char** argv)
{
EpmdVars g_empd_vars;
EpmdVars *g = &g_empd_vars;
+ int i;
#ifdef __WIN32__
WORD wVersionRequested;
WSADATA wsaData;
@@ -156,21 +160,25 @@ int main(int argc, char** argv)
g->argv = NULL;
#endif
- g->port = get_port_no();
- g->debug = 0;
+ g->addresses = get_addresses();
+ g->port = get_port_no();
+ g->debug = 0;
g->silent = 0;
g->is_daemon = 0;
+ g->brutal_kill = check_relaxed();
g->packet_timeout = CLOSE_TIMEOUT; /* Default timeout */
g->delay_accept = 0;
g->delay_write = 0;
g->progname = argv[0];
- g->listenfd = -1;
g->conn = NULL;
g->nodes.reg = g->nodes.unreg = g->nodes.unreg_tail = NULL;
g->nodes.unreg_count = 0;
g->active_conn = 0;
+ for (i = 0; i < MAX_LISTEN_SOCKETS; i++)
+ g->listenfd[i] = -1;
+
argc--;
argv++;
while (argc > 0) {
@@ -196,12 +204,20 @@ int main(int argc, char** argv)
} else if (strcmp(argv[0], "-daemon") == 0) {
g->is_daemon = 1;
argv++; argc--;
+ } else if (strcmp(argv[0], "-relaxed_command_check") == 0) {
+ g->brutal_kill = 1;
+ argv++; argc--;
} else if (strcmp(argv[0], "-kill") == 0) {
if (argc == 1)
kill_epmd(g);
else
usage(g);
epmd_cleanup_exit(g,0);
+ } else if (strcmp(argv[0], "-address") == 0) {
+ if (argc == 1)
+ usage(g);
+ g->addresses = argv[1];
+ argv += 2; argc -= 2;
} else if (strcmp(argv[0], "-port") == 0) {
if ((argc == 1) ||
((g->port = atoi(argv[1])) == 0))
@@ -246,13 +262,10 @@ int main(int argc, char** argv)
/*
* max_conn must not be greater than FD_SETSIZE.
* (at least QNX crashes)
- *
- * More correctly, it must be FD_SETSIZE - 1, beacuse the
- * listen FD is stored outside the connection array.
*/
if (g->max_conn > FD_SETSIZE) {
- g->max_conn = FD_SETSIZE - 1;
+ g->max_conn = FD_SETSIZE;
}
if (g->is_daemon) {
@@ -387,10 +400,14 @@ static void run_daemon(EpmdVars *g)
static void usage(EpmdVars *g)
{
- fprintf(stderr, "usage: epmd [-d|-debug] [DbgExtra...] [-port No] [-daemon]\n");
- fprintf(stderr, " [-d|-debug] [-port No] [-names|-kill|-stop name]\n\n");
- fprintf(stderr, "See the Erlang epmd manual page for info about the usage.\n");
- fprintf(stderr, "The -port and DbgExtra options are\n\n");
+ fprintf(stderr, "usage: epmd [-d|-debug] [DbgExtra...] [-address List]\n");
+ fprintf(stderr, " [-port No] [-daemon] [-relaxed_command_check]\n");
+ fprintf(stderr, " epmd [-d|-debug] [-port No] [-names|-kill|-stop name]\n\n");
+ fprintf(stderr, "See the Erlang epmd manual page for info about the usage.\n\n");
+ fprintf(stderr, "Regular options\n");
+ fprintf(stderr, " -address List\n");
+ fprintf(stderr, " Let epmd listen only on the comma-separated list of IP\n");
+ fprintf(stderr, " addresses (and on the loopback interface).\n");
fprintf(stderr, " -port No\n");
fprintf(stderr, " Let epmd listen to another port than default %d\n",
EPMD_PORT_NO);
@@ -400,8 +417,16 @@ static void usage(EpmdVars *g)
fprintf(stderr, " the standard error stream. It will shorten\n");
fprintf(stderr, " the number of saved used node names to 5.\n\n");
fprintf(stderr, " If you give more than one debug flag you may\n");
- fprintf(stderr, " get more debugging information.\n\n");
- fprintf(stderr, " -packet_timout Seconds\n");
+ fprintf(stderr, " get more debugging information.\n");
+ fprintf(stderr, " -daemon\n");
+ fprintf(stderr, " Start epmd detached (as a daemon)\n");
+ fprintf(stderr, " -relaxed_command_check\n");
+ fprintf(stderr, " Allow this instance of epmd to be killed with\n");
+ fprintf(stderr, " epmd -kill even if there "
+ "are registered nodes.\n");
+ fprintf(stderr, " Also allows forced unregister (epmd -stop).\n");
+ fprintf(stderr, "\nDbgExtra options\n");
+ fprintf(stderr, " -packet_timeout Seconds\n");
fprintf(stderr, " Set the number of seconds a connection can be\n");
fprintf(stderr, " inactive before epmd times out and closes the\n");
fprintf(stderr, " connection (default 60).\n\n");
@@ -413,6 +438,18 @@ static void usage(EpmdVars *g)
fprintf(stderr, " -delay_write Seconds\n");
fprintf(stderr, " Also a simulation of a busy server. Inserts\n");
fprintf(stderr, " a delay before a reply is sent.\n");
+ fprintf(stderr, "\nInteractive options\n");
+ fprintf(stderr, " -names\n");
+ fprintf(stderr, " List names registered with the currently "
+ "running epmd\n");
+ fprintf(stderr, " -kill\n");
+ fprintf(stderr, " Kill the currently running epmd\n");
+ fprintf(stderr, " (only allowed if -names show empty database or\n");
+ fprintf(stderr, " -relaxed_command_check was given when epmd was started).\n");
+ fprintf(stderr, " -stop Name\n");
+ fprintf(stderr, " Forcibly unregisters a name with epmd\n");
+ fprintf(stderr, " (only allowed if -relaxed_command_check was given when \n");
+ fprintf(stderr, " epmd was started).\n");
epmd_cleanup_exit(g,1);
}
@@ -432,20 +469,20 @@ static void usage(EpmdVars *g)
* args... Arguments to print out according to the format
*
*/
-
+#define DEBUG_BUFFER_SIZE 2048
static void dbg_gen_printf(int onsyslog,int perr,int from_level,
EpmdVars *g,const char *format, va_list args)
{
time_t now;
char *timestr;
- char buf[2048];
+ char buf[DEBUG_BUFFER_SIZE];
if (g->is_daemon)
{
#ifndef NO_SYSLOG
if (onsyslog)
{
- vsprintf(buf, format, args);
+ erts_vsnprintf(buf, DEBUG_BUFFER_SIZE, format, args);
syslog(LOG_ERR,"epmd: %s",buf);
}
#endif
@@ -456,11 +493,12 @@ static void dbg_gen_printf(int onsyslog,int perr,int from_level,
time(&now);
timestr = (char *)ctime(&now);
- sprintf(buf, "epmd: %.*s: ", (int) strlen(timestr)-1, timestr);
+ erts_snprintf(buf, DEBUG_BUFFER_SIZE, "epmd: %.*s: ",
+ (int) strlen(timestr)-1, timestr);
len = strlen(buf);
- vsprintf(buf + len, format, args);
- if (perr == 1)
- perror(buf);
+ erts_vsnprintf(buf + len, DEBUG_BUFFER_SIZE - len, format, args);
+ if (perr != 0)
+ fprintf(stderr,"%s: %s\r\n",buf,strerror(perr));
else
fprintf(stderr,"%s\r\n",buf);
}
@@ -471,7 +509,7 @@ void dbg_perror(EpmdVars *g,const char *format,...)
{
va_list args;
va_start(args, format);
- dbg_gen_printf(1,1,0,g,format,args);
+ dbg_gen_printf(1,errno,0,g,format,args);
va_end(args);
}
@@ -527,8 +565,9 @@ void epmd_cleanup_exit(EpmdVars *g, int exitval)
epmd_conn_close(g,&g->conn[i]);
free(g->conn);
}
- if(g->listenfd >= 0)
- close(g->listenfd);
+ for(i=0; i < MAX_LISTEN_SOCKETS; i++)
+ if(g->listenfd[i] >= 0)
+ close(g->listenfd[i]);
free_all_nodes(g);
if(g->argv){
for(i=0; g->argv[i] != NULL; ++i)
@@ -540,9 +579,18 @@ void epmd_cleanup_exit(EpmdVars *g, int exitval)
exit(exitval);
}
+static char* get_addresses(void)
+{
+ return getenv("ERL_EPMD_ADDRESS");
+}
static int get_port_no(void)
{
char* port_str = getenv("ERL_EPMD_PORT");
return (port_str != NULL) ? atoi(port_str) : EPMD_PORT_NO;
}
+static int check_relaxed(void)
+{
+ char* port_str = getenv("ERL_EPMD_RELAXED_COMMAND_CHECK");
+ return (port_str != NULL) ? 1 : 0;
+}
diff --git a/erts/epmd/src/epmd.h b/erts/epmd/src/epmd.h
index 9e939ee38e..5d6e9ac165 100644
--- a/erts/epmd/src/epmd.h
+++ b/erts/epmd/src/epmd.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2010. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -17,21 +17,18 @@
* %CopyrightEnd%
*/
-/* The port number is now defined in a makefile */
+/* The port number is defined in a makefile */
/* Definitions of message codes */
-#define EPMD_ALIVE_REQ 'a'
-#define EPMD_ALIVE_OK_RESP 'Y'
-#define EPMD_PORT_REQ 'p'
+/* Registration and queries */
+#define EPMD_ALIVE2_REQ 'x'
+#define EPMD_PORT2_REQ 'z'
+#define EPMD_ALIVE2_RESP 'y'
+#define EPMD_PORT2_RESP 'w'
#define EPMD_NAMES_REQ 'n'
+
+/* Interactive client command codes */
#define EPMD_DUMP_REQ 'd'
#define EPMD_KILL_REQ 'k'
#define EPMD_STOP_REQ 's'
-
-/* New epmd messages */
-
-#define EPMD_ALIVE2_REQ 'x' /* 120 */
-#define EPMD_PORT2_REQ 'z' /* 122 */
-#define EPMD_ALIVE2_RESP 'y' /* 121 */
-#define EPMD_PORT2_RESP 'w' /* 119 */
diff --git a/erts/epmd/src/epmd_cli.c b/erts/epmd/src/epmd_cli.c
index 2aed861390..ac55ba6bb6 100644
--- a/erts/epmd/src/epmd_cli.c
+++ b/erts/epmd/src/epmd_cli.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2010. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -42,7 +42,11 @@ void kill_epmd(EpmdVars *g)
epmd_cleanup_exit(g,1);
}
if ((rval = read_fill(fd,buf,2)) == 2) {
- printf("Killed\n");
+ if (buf[0] == 'O' && buf[1] == 'K') {
+ printf("Killed\n");
+ } else {
+ printf("Killing not allowed - living nodes in database.\n");
+ }
epmd_cleanup_exit(g,0);
} else if (rval < 0) {
printf("epmd: failed to read answer from local epmd\n");
@@ -133,7 +137,7 @@ static int conn_to_epmd(EpmdVars *g)
{ /* store port number in unsigned short */
unsigned short sport = g->port;
- SET_ADDR_LOOPBACK(address, FAMILY, sport);
+ SET_ADDR(address, EPMD_ADDR_LOOPBACK, sport);
}
if (connect(connect_sock, (struct sockaddr*)&address, sizeof address) < 0)
diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h
index 5ead553f36..2a0de4df9c 100644
--- a/erts/epmd/src/epmd_int.h
+++ b/erts/epmd/src/epmd_int.h
@@ -1,3 +1,4 @@
+/* -*- c-indent-level: 2; c-continued-statement-offset: 2 -*- */
/*
* %CopyrightBegin%
*
@@ -167,42 +168,40 @@
#if defined(HAVE_IN6) && defined(AF_INET6) && defined(EPMD6)
#define EPMD_SOCKADDR_IN sockaddr_in6
-#define FAMILY AF_INET6
-
-#define SET_ADDR_LOOPBACK(addr, af, port) do { \
- memset((char*)&(addr), 0, sizeof(addr)); \
- (addr).sin6_family = (af); \
- (addr).sin6_flowinfo = 0; \
- (addr).sin6_addr = in6addr_loopback; \
- (addr).sin6_port = htons(port); \
+#define EPMD_IN_ADDR in6_addr
+#define EPMD_S_ADDR s6_addr
+#define EPMD_ADDR_LOOPBACK in6addr_loopback.s6_addr
+#define EPMD_ADDR_ANY in6addr_any.s6_addr
+#define FAMILY AF_INET6
+
+#define SET_ADDR(dst, addr, port) do { \
+ memset((char*)&(dst), 0, sizeof(dst)); \
+ memcpy((char*)&(dst).sin6_addr.s6_addr, (char*)&(addr), 16); \
+ (dst).sin6_family = AF_INET6; \
+ (dst).sin6_flowinfo = 0; \
+ (dst).sin6_port = htons(port); \
} while(0)
-#define SET_ADDR_ANY(addr, af, port) do { \
- memset((char*)&(addr), 0, sizeof(addr)); \
- (addr).sin6_family = (af); \
- (addr).sin6_flowinfo = 0; \
- (addr).sin6_addr = in6addr_any; \
- (addr).sin6_port = htons(port); \
- } while(0)
+#define IS_ADDR_LOOPBACK(addr) \
+ (memcmp((addr).s6_addr, in6addr_loopback.s6_addr, 16) == 0)
#else /* Not IP v6 */
#define EPMD_SOCKADDR_IN sockaddr_in
-#define FAMILY AF_INET
-
-#define SET_ADDR_LOOPBACK(addr, af, port) do { \
- memset((char*)&(addr), 0, sizeof(addr)); \
- (addr).sin_family = (af); \
- (addr).sin_addr.s_addr = htonl(INADDR_LOOPBACK); \
- (addr).sin_port = htons(port); \
+#define EPMD_IN_ADDR in_addr
+#define EPMD_S_ADDR s_addr
+#define EPMD_ADDR_LOOPBACK htonl(INADDR_LOOPBACK)
+#define EPMD_ADDR_ANY htonl(INADDR_ANY)
+#define FAMILY AF_INET
+
+#define SET_ADDR(dst, addr, port) do { \
+ memset((char*)&(dst), 0, sizeof(dst)); \
+ (dst).sin_family = AF_INET; \
+ (dst).sin_addr.s_addr = (addr); \
+ (dst).sin_port = htons(port); \
} while(0)
-#define SET_ADDR_ANY(addr, af, port) do { \
- memset((char*)&(addr), 0, sizeof(addr)); \
- (addr).sin_family = (af); \
- (addr).sin_addr.s_addr = htonl(INADDR_ANY); \
- (addr).sin_port = htons(port); \
- } while(0)
+#define IS_ADDR_LOOPBACK(addr) ((addr).s_addr == htonl(INADDR_LOOPBACK))
#endif /* Not IP v6 */
@@ -230,6 +229,8 @@
/* Maximum length of a node name == atom name */
#define MAXSYMLEN 255
+#define MAX_LISTEN_SOCKETS 16
+
#define INBUF_SIZE 1024
#define OUTBUF_SIZE 1024
@@ -245,8 +246,10 @@
typedef struct {
int fd; /* File descriptor */
- unsigned open:1; /* TRUE if open */
- unsigned keep:1; /* Don't close when sent reply */
+ unsigned char open; /* TRUE if open */
+ unsigned char keep; /* Don't close when sent reply */
+ unsigned char local_peer; /* The peer of this connection is via
+ loopback interface */
unsigned got; /* # of bytes we have got */
unsigned want; /* Number of bytes we want */
char *buf; /* The remaining buffer */
@@ -286,6 +289,7 @@ typedef struct {
int debug;
int silent;
int is_daemon;
+ int brutal_kill;
unsigned packet_timeout;
unsigned delay_accept;
unsigned delay_write;
@@ -295,7 +299,8 @@ typedef struct {
Connection *conn;
Nodes nodes;
fd_set orig_read_mask;
- int listenfd;
+ int listenfd[MAX_LISTEN_SOCKETS];
+ char *addresses;
char **argv;
} EpmdVars;
@@ -307,6 +312,7 @@ void epmd_call(EpmdVars*,int);
void run(EpmdVars*);
void epmd_cleanup_exit(EpmdVars*, int);
int epmd_conn_close(EpmdVars*,Connection*);
+void stop_cli(EpmdVars *g, char *name);
#ifdef DONT_USE_MAIN
int start_epmd(char *,char *,char *,char *,char *,char *,char *,char *,char *,char *);
diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c
index c836bf0bb7..4d9b454f97 100644
--- a/erts/epmd/src/epmd_srv.c
+++ b/erts/epmd/src/epmd_srv.c
@@ -2,7 +2,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2010. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -24,6 +24,10 @@
#include "epmd.h" /* Renamed from 'epmd_r4.h' */
#include "epmd_int.h"
+#ifndef INADDR_NONE
+# define INADDR_NONE 0xffffffff
+#endif
+
/*
*
* This server is a local name server for Erlang nodes. Erlang nodes can
@@ -39,46 +43,16 @@
* server keeps the socket open where the request for registration was
* made.
*
- * The protocol is briefly documented in "erl_ext_dist.txt". All requests
- * to this server are done with a packet
+ * The protocol is briefly documented in the ERTS User's Guide, see
+ * http://www.erlang.org/doc/apps/erts/erl_dist_protocol.html
+ *
+ * All requests to this server are done with a packet
*
* 2 n
* +--------+---------+
* | Length | Request |
* +--------+---------+
*
- * In all but one case there is only one request for each connection made
- * to this server so we can safely close the socket after sending the
- * reply. The exception is ALIVE_REQ where we keep the connection
- * open without sending any data. When we receive a "close" this is
- * an indication that the Erlang node was terminated. The termination
- * may have been "normal" or caused by a crash. The operating system
- * ensure that the connection is closed either way.
- *
- * Reading is done non-blocking, i.e. we call a "read" only if we are
- * told by the "select" function that there are data to read.
- *
- * Two databases are used: One node database where the registered names
- * of the nodes are stored, and one connection database where the state
- * of sockets and the data buffers is stored.
- *
- * Incomplete packets are thrown away after a timout. The Erlang node
- * doing the request is responsible for completing in it in a reasonable time.
- *
- * Note that if the server gets busy it may not have time to
- * process all requests for connection. The "accept()" function
- * will on most operating systems silently refuse to accept more
- * than 5 outstanding requests. It is the client's responsibility
- * to retry the request a number of times with random time interval.
- * The "-debug" flag will insert a delay so you can test this
- * behaviour.
- *
- * FIXME: In this code we assume that the packets we send on each
- * socket is so small that a "write()" never block
- *
- * FIXME: We never restarts a read or write that was terminated
- * by an interrupt. Do we need to?
- *
*/
/* We use separate data structures for node names and connections
@@ -98,7 +72,6 @@ static int conn_open(EpmdVars*,int);
static int conn_close_fd(EpmdVars*,int);
static void node_init(EpmdVars*);
-static Node *node_reg(EpmdVars*,char*,int,int);
static Node *node_reg2(EpmdVars*,char*, int, int, unsigned char, unsigned char, int, int, int, char*);
static int node_unreg(EpmdVars*,char*);
static int node_unreg_sock(EpmdVars*,int);
@@ -110,89 +83,157 @@ static void print_names(EpmdVars*);
void run(EpmdVars *g)
{
- int listensock;
+ struct EPMD_SOCKADDR_IN iserv_addr[MAX_LISTEN_SOCKETS];
+ int listensock[MAX_LISTEN_SOCKETS];
+ int num_sockets;
int i;
int opt;
- struct EPMD_SOCKADDR_IN iserv_addr;
+ unsigned short sport = g->port;
node_init(g);
g->conn = conn_init(g);
dbg_printf(g,2,"try to initiate listening port %d", g->port);
-
- if ((listensock = socket(FAMILY,SOCK_STREAM,0)) < 0) {
- dbg_perror(g,"error opening stream socket");
- epmd_cleanup_exit(g,1);
- }
- g->listenfd = listensock;
+
+ if (g->addresses != NULL)
+ {
+ char *tmp;
+ char *token;
+ int loopback_ok = 0;
+
+ if ((tmp = (char *)malloc(strlen(g->addresses) + 1)) == NULL)
+ {
+ dbg_perror(g,"cannot allocate memory");
+ epmd_cleanup_exit(g,1);
+ }
+ strcpy(tmp,g->addresses);
+
+ for(token = strtok(tmp,", "), num_sockets = 0;
+ token != NULL;
+ token = strtok(NULL,", "), num_sockets++)
+ {
+ struct EPMD_IN_ADDR addr;
+#ifdef HAVE_INET_PTON
+ int ret;
+
+ if ((ret = inet_pton(FAMILY,token,&addr)) == -1)
+ {
+ dbg_perror(g,"cannot convert IP address to network format");
+ epmd_cleanup_exit(g,1);
+ }
+ else if (ret == 0)
+#elif !defined(EPMD6)
+ if ((addr.EPMD_S_ADDR = inet_addr(token)) == INADDR_NONE)
+#endif
+ {
+ dbg_tty_printf(g,0,"cannot parse IP address \"%s\"",token);
+ epmd_cleanup_exit(g,1);
+ }
+
+ if (IS_ADDR_LOOPBACK(addr))
+ loopback_ok = 1;
+
+ if (num_sockets - loopback_ok == MAX_LISTEN_SOCKETS - 1)
+ {
+ dbg_tty_printf(g,0,"cannot listen on more than %d IP addresses",
+ MAX_LISTEN_SOCKETS);
+ epmd_cleanup_exit(g,1);
+ }
+
+ SET_ADDR(iserv_addr[num_sockets],addr.EPMD_S_ADDR,sport);
+ }
+
+ free(tmp);
+
+ if (!loopback_ok)
+ {
+ SET_ADDR(iserv_addr[num_sockets],EPMD_ADDR_LOOPBACK,sport);
+ num_sockets++;
+ }
+ }
+ else
+ {
+ SET_ADDR(iserv_addr[0],EPMD_ADDR_ANY,sport);
+ num_sockets = 1;
+ }
+
+#if !defined(__WIN32__)
+ /* We ignore the SIGPIPE signal that is raised when we call write
+ twice on a socket closed by the other end. */
+ signal(SIGPIPE, SIG_IGN);
+#endif
/*
* Initialize number of active file descriptors.
* Stdin, stdout, and stderr are still open.
- * One for the listen socket.
*/
- g->active_conn = 3+1;
+ g->active_conn = 3 + num_sockets;
+ g->max_conn -= num_sockets;
+
+ FD_ZERO(&g->orig_read_mask);
+
+ for (i = 0; i < num_sockets; i++)
+ {
+ if ((listensock[i] = socket(FAMILY,SOCK_STREAM,0)) < 0)
+ {
+ dbg_perror(g,"error opening stream socket");
+ epmd_cleanup_exit(g,1);
+ }
+ g->listenfd[i] = listensock[i];
- /*
- * Note that we must not enable the SO_REUSEADDR on Windows,
- * because addresses will be reused even if they are still in use.
- */
+ /*
+ * Note that we must not enable the SO_REUSEADDR on Windows,
+ * because addresses will be reused even if they are still in use.
+ */
#if !defined(__WIN32__)
- /* We ignore the SIGPIPE signal that is raised when we call write
- twice on a socket closed by the other end. */
- signal(SIGPIPE, SIG_IGN);
-
- opt = 1; /* Set this option */
- if (setsockopt(listensock,SOL_SOCKET,SO_REUSEADDR,(char* ) &opt,
- sizeof(opt)) <0) {
- dbg_perror(g,"can't set sockopt");
- epmd_cleanup_exit(g,1);
- }
+ opt = 1;
+ if (setsockopt(listensock[i],SOL_SOCKET,SO_REUSEADDR,(char* ) &opt,
+ sizeof(opt)) <0)
+ {
+ dbg_perror(g,"can't set sockopt");
+ epmd_cleanup_exit(g,1);
+ }
#endif
- /* In rare cases select returns because there is someone
- to accept but the request is withdrawn before the
- accept function is called. We set the listen socket
- to be non blocking to prevent us from being hanging
- in accept() waiting for the next request. */
+ /* In rare cases select returns because there is someone
+ to accept but the request is withdrawn before the
+ accept function is called. We set the listen socket
+ to be non blocking to prevent us from being hanging
+ in accept() waiting for the next request. */
#if (defined(__WIN32__) || defined(NO_FCNTL))
- opt = 1;
- if (ioctl(listensock, FIONBIO, &opt) != 0) /* Gives warning in VxWorks */
+ opt = 1;
+ /* Gives warning in VxWorks */
+ if (ioctl(listensock[i], FIONBIO, &opt) != 0)
#else
- opt = fcntl(listensock, F_GETFL, 0);
- if (fcntl(listensock, F_SETFL, opt | O_NONBLOCK) == -1)
+ opt = fcntl(listensock[i], F_GETFL, 0);
+ if (fcntl(listensock[i], F_SETFL, opt | O_NONBLOCK) == -1)
#endif /* __WIN32__ || VXWORKS */
- dbg_perror(g,"failed to set non-blocking mode of listening socket %d",
- listensock);
+ dbg_perror(g,"failed to set non-blocking mode of listening socket %d",
+ listensock[i]);
- { /* store port number in unsigned short */
- unsigned short sport = g->port;
- SET_ADDR_ANY(iserv_addr, FAMILY, sport);
- }
-
- if(bind(listensock,(struct sockaddr*) &iserv_addr, sizeof(iserv_addr)) < 0 )
- {
- if (errno == EADDRINUSE)
+ if (bind(listensock[i], (struct sockaddr*) &iserv_addr[i],
+ sizeof(iserv_addr[i])) < 0)
{
- dbg_tty_printf(g,1,"there is already a epmd running at port %d",
- g->port);
- epmd_cleanup_exit(g,0);
- }
- else
- {
- dbg_perror(g,"failed to bind socket");
- epmd_cleanup_exit(g,1);
+ if (errno == EADDRINUSE)
+ {
+ dbg_tty_printf(g,1,"there is already a epmd running at port %d",
+ g->port);
+ epmd_cleanup_exit(g,0);
+ }
+ else
+ {
+ dbg_perror(g,"failed to bind socket");
+ epmd_cleanup_exit(g,1);
+ }
}
- }
- dbg_printf(g,2,"starting");
-
- listen(listensock, SOMAXCONN);
-
-
- FD_ZERO(&g->orig_read_mask);
- FD_SET(listensock,&g->orig_read_mask);
+ if(listen(listensock[i], SOMAXCONN) < 0) {
+ dbg_perror(g,"failed to listen on socket");
+ epmd_cleanup_exit(g,1);
+ }
+ FD_SET(listensock[i],&g->orig_read_mask);
+ }
dbg_tty_printf(g,2,"entering the main select() loop");
@@ -229,17 +270,18 @@ void run(EpmdVars *g)
sleep(g->delay_accept);
}
- if (FD_ISSET(listensock,&read_mask)) {
- if (do_accept(g, listensock) && g->active_conn < g->max_conn) {
- /*
- * The accept() succeeded, and we have at least one file
- * descriptor still free, which means that another accept()
- * could succeed. Go do do another select(), in case there
- * are more incoming connections waiting to be accepted.
- */
- goto select_again;
+ for (i = 0; i < num_sockets; i++)
+ if (FD_ISSET(listensock[i],&read_mask)) {
+ if (do_accept(g, listensock[i]) && g->active_conn < g->max_conn) {
+ /*
+ * The accept() succeeded, and we have at least one file
+ * descriptor still free, which means that another accept()
+ * could succeed. Go do do another select(), in case there
+ * are more incoming connections waiting to be accepted.
+ */
+ goto select_again;
+ }
}
- }
/* Check all open streams marked by select for data or a
close. We also close all open sockets except ALIVE
@@ -303,11 +345,9 @@ static void do_read(EpmdVars *g,Connection *s)
s->fd,val);
dbg_print_buf(g,s->buf,val);
- /* FIXME: Shouldn't be needed to close down.... */
node_unreg_sock(g,s->fd);
epmd_conn_close(g,s);
}
- /* FIXME: We always close, probably the right thing to do */
return;
}
@@ -405,6 +445,8 @@ static int do_accept(EpmdVars *g,int listensock)
return conn_open(g,msgsock);
}
+/* buf is actually one byte larger than bsize,
+ giving place for null termination */
static void do_request(g, fd, s, buf, bsize)
EpmdVars *g;
int fd;
@@ -415,117 +457,23 @@ static void do_request(g, fd, s, buf, bsize)
char wbuf[OUTBUF_SIZE]; /* Buffer for writing */
int i;
- /*
- * Terminate packet as a C string. Needed for requests received from Erlang
- * nodes with lower version than R3A.
- */
-
- buf[bsize] = '\0';
+ buf[bsize] = '\0'; /* Needed for strcmp in PORT2 and STOP requests
+ buf is always large enough */
switch (*buf)
{
- case EPMD_ALIVE_REQ:
- dbg_printf(g,1,"** got ALIVE_REQ");
-
- /* The packet has the format "axxyyyyyy" where xx is port, given
- in network byte order, and yyyyyy is symname, possibly null
- terminated. */
-
- if (buf[bsize - 1] == '\000') /* Skip null termination */
- bsize--;
-
- if (bsize <= 3)
- {
- dbg_printf(g,0,"packet to small for request ALIVE_REQ (%d)", bsize);
- return;
- }
-
- for (i = 3; i < bsize; i++)
- if (buf[i] == '\000')
- {
- dbg_printf(g,0,"node name contains ascii 0 in ALIVE_REQ");
- return;
- }
-
- {
- Node *node;
- int eport;
- char *name = &buf[3]; /* points to node name */
-
- eport = get_int16(&buf[1]);
-
- if ((node = node_reg(g, name, fd, eport)) == NULL)
- return;
-
- wbuf[0] = EPMD_ALIVE_OK_RESP;
- put_int16(node->creation, wbuf+1);
-
- if (g->delay_write) /* Test of busy server */
- sleep(g->delay_write);
-
- if (reply(g, fd, wbuf, 3) != 3)
- {
- dbg_tty_printf(g,1,"failed to send ALIVE_OK_RESP for \"%s\"",name);
- return;
- }
-
- dbg_tty_printf(g,1,"** sent ALIVE_OK_RESP for \"%s\"",name);
- s->keep = EPMD_TRUE; /* Don't close on inactivity */
- }
- break;
-
- case EPMD_PORT_REQ:
- dbg_printf(g,1,"** got PORT_REQ");
-
- if (buf[bsize - 1] == '\000') /* Skip null termination */
- bsize--;
-
- if (bsize <= 1)
- {
- dbg_printf(g,0,"packet to small for request PORT_REQ (%d)", bsize);
- return;
- }
-
- for (i = 1; i < bsize; i++)
- if (buf[i] == '\000')
- {
- dbg_printf(g,0,"node name contains ascii 0 in PORT_REQ");
- return;
- }
-
- {
- char *name = &buf[1]; /* Points to node name */
- Node *node;
-
- for (node = g->nodes.reg; node; node = node->next)
- {
- if (strcmp(node->symname, name) == 0)
- {
- put_int16(node->port,wbuf);
- if (reply(g, fd, wbuf, 2) != 2)
- {
- dbg_tty_printf(g,1,"failed to send PORT_RESP for %s: %d",
- name,node->port);
- return;
- }
- dbg_tty_printf(g,1,"** sent PORT_RESP for %s: %d",
- name,node->port);
- return;
- }
- }
- dbg_tty_printf(g,1,"Closed on PORT_REQ for %s",name);
- }
- /* FIXME: How about an answer if no port? Is a close enough? */
- break;
-
case EPMD_ALIVE2_REQ:
dbg_printf(g,1,"** got ALIVE2_REQ");
+ if (!s->local_peer) {
+ dbg_printf(g,0,"ALIVE2_REQ from non local address");
+ return;
+ }
/* The packet has the format "axxyyyyyy" where xx is port, given
in network byte order, and yyyyyy is symname, possibly null
terminated. */
- if (bsize <= 13)
+ if (bsize <= 13) /* at least one character for the node name */
{
dbg_printf(g,0,"packet to small for request ALIVE2_REQ (%d)",bsize);
return;
@@ -548,7 +496,17 @@ static void do_request(g, fd, s, buf, bsize)
highvsn = get_int16(&buf[5]);
lowvsn = get_int16(&buf[7]);
namelen = get_int16(&buf[9]);
+ if (namelen + 13 > bsize) {
+ dbg_printf(g,0,"Node name size error in ALIVE2_REQ");
+ return;
+ }
extralen = get_int16(&buf[11+namelen]);
+
+ if (extralen + namelen + 13 > bsize) {
+ dbg_printf(g,0,"Extra info size error in ALIVE2_REQ");
+ return;
+ }
+
for (i = 11 ; i < 11 + namelen; i ++)
if (buf[i] == '\000') {
dbg_printf(g,0,"node name contains ascii 0 in ALIVE2_REQ");
@@ -679,6 +637,10 @@ static void do_request(g, fd, s, buf, bsize)
case EPMD_DUMP_REQ:
dbg_printf(g,1,"** got DUMP_REQ");
+ if (!s->local_peer) {
+ dbg_printf(g,0,"DUMP_REQ from non local address");
+ return;
+ }
{
Node *node;
@@ -728,7 +690,19 @@ static void do_request(g, fd, s, buf, bsize)
break;
case EPMD_KILL_REQ:
+ if (!s->local_peer) {
+ dbg_printf(g,0,"KILL_REQ from non local address");
+ return;
+ }
dbg_printf(g,1,"** got KILL_REQ");
+
+ if (!g->brutal_kill && (g->nodes.reg != NULL)) {
+ dbg_printf(g,0,"Disallowed KILL_REQ, live nodes");
+ if (reply(g, fd,"NO",2) != 2)
+ dbg_printf(g,0,"failed to send reply to KILL_REQ");
+ return;
+ }
+
if (reply(g, fd,"OK",2) != 2)
dbg_printf(g,0,"failed to send reply to KILL_REQ");
dbg_tty_printf(g,1,"epmd killed");
@@ -738,6 +712,15 @@ static void do_request(g, fd, s, buf, bsize)
case EPMD_STOP_REQ:
dbg_printf(g,1,"** got STOP_REQ");
+ if (!s->local_peer) {
+ dbg_printf(g,0,"STOP_REQ from non local address");
+ return;
+ }
+ if (!g->brutal_kill) {
+ dbg_printf(g,0,"Disallowed STOP_REQ, no relaxed_command_check");
+ return;
+ }
+
if (bsize <= 1 )
{
dbg_printf(g,0,"packet too small for request STOP_REQ (%d)",bsize);
@@ -825,6 +808,15 @@ static int conn_open(EpmdVars *g,int fd)
for (i = 0; i < g->max_conn; i++) {
if (g->conn[i].open == EPMD_FALSE) {
+ struct sockaddr_in si;
+ struct sockaddr_in di;
+#ifdef HAVE_SOCKLEN_T
+ socklen_t st;
+#else
+ int st;
+#endif
+ st = sizeof(si);
+
g->active_conn++;
s = &g->conn[i];
@@ -834,6 +826,24 @@ static int conn_open(EpmdVars *g,int fd)
s->fd = fd;
s->open = EPMD_TRUE;
s->keep = EPMD_FALSE;
+
+ /* Determine if connection is from localhost */
+ if (getpeername(s->fd,(struct sockaddr*) &si,&st) ||
+ st < sizeof(si)) {
+ /* Failure to get peername is regarded as non local host */
+ s->local_peer = EPMD_FALSE;
+ } else {
+ /* Only 127.x.x.x and connections from the host's IP address
+ allowed, no false positives */
+ s->local_peer =
+ (((((unsigned) ntohl(si.sin_addr.s_addr)) & 0xFF000000U) ==
+ 0x7F000000U) ||
+ (getsockname(s->fd,(struct sockaddr*) &di,&st) ?
+ EPMD_FALSE : si.sin_addr.s_addr == di.sin_addr.s_addr));
+ }
+ dbg_tty_printf(g,2,(s->local_peer) ? "Local peer connected" :
+ "Non-local peer connected");
+
s->want = 0; /* Currently unknown */
s->got = 0;
s->mod_time = current_time(g); /* Note activity */
@@ -990,11 +1000,6 @@ static int node_unreg_sock(EpmdVars *g,int fd)
* Perhaps use the oldest or something.
*/
-static Node *node_reg(EpmdVars *g,char *name,int fd, int port)
-{
- return node_reg2(g, name, fd, port, 0, 0, 0, 0, 0, NULL);
-}
-
static Node *node_reg2(EpmdVars *g,
char* name,
int fd,
@@ -1020,6 +1025,11 @@ static Node *node_reg2(EpmdVars *g,
dbg_printf(g,0,"node name is too long (%d) %s", strlen(name), name);
return NULL;
}
+ if (extralen > MAXSYMLEN)
+ {
+ dbg_printf(g,0,"extra data is too long (%d) %s", strlen(name), name);
+ return NULL;
+ }
/* Fail if it is already registered */