aboutsummaryrefslogtreecommitdiffstats
path: root/erts/epmd
diff options
context:
space:
mode:
Diffstat (limited to 'erts/epmd')
-rw-r--r--erts/epmd/src/Makefile.in29
-rw-r--r--erts/epmd/src/epmd.c164
-rw-r--r--erts/epmd/src/epmd.h21
-rw-r--r--erts/epmd/src/epmd_cli.c44
-rw-r--r--erts/epmd/src/epmd_int.h53
-rw-r--r--erts/epmd/src/epmd_srv.c262
-rw-r--r--erts/epmd/test/epmd_SUITE.erl356
7 files changed, 516 insertions, 413 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 23ac421446..65ff0cd6b2 100644
--- a/erts/epmd/src/epmd.c
+++ b/erts/epmd/src/epmd.c
@@ -1,20 +1,20 @@
/* -*- c-indent-level: 2; c-continued-statement-offset: 2 -*- */
/*
* %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
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
- *
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
+ *
* %CopyrightEnd%
*/
@@ -23,11 +23,7 @@
#endif
#include "epmd.h" /* Renamed from 'epmd_r4.h' */
#include "epmd_int.h"
-
-#ifdef _OSE_
-# include "ose.h"
-# include "efs.h"
-#endif
+#include "erl_printf.h"
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
@@ -38,6 +34,7 @@
static void usage(EpmdVars *);
static void run_daemon(EpmdVars*);
static int get_port_no(void);
+static int check_relaxed(void);
#ifdef __WIN32__
static int has_console(void);
#endif
@@ -82,86 +79,7 @@ static char *mystrdup(char *s)
return r;
}
-#ifdef _OSE_
-
-struct args_sig {
- SIGSELECT sig_no;
- int argc ;
- char argv[20][20];
-};
-
-union SIGNAL {
- SIGSELECT sig_no;
- struct args_sig args;
-};
-
-/* Start function. It may be called from the start script as well as from
- the OSE shell directly (using late start hooks). It spawns epmd as an
- OSE process which calls the epmd main function. */
-int start_ose_epmd(int argc, char **argv) {
- union SIGNAL *sig;
- PROCESS epmd_;
- OSENTRYPOINT ose_epmd;
- int i;
-
- if(hunt("epmd", 0, &epmd_, NULL)) {
- fprintf(stderr, "Warning! EPMD already exists (%u).\n", epmd_);
- return 0;
- }
- else {
- /* copy start args to signal */
- sig = alloc(sizeof(struct args_sig), 0);
- sig->args.argc = argc;
- for(i=0; i<argc; i++) {
- strcpy((sig->args.argv)[i], argv[i]);
- }
- /* start epmd and send signal */
- epmd_ = create_process(OS_BG_PROC, /* processtype */
- "epmd", /* name */
- ose_epmd, /* entrypoint */
- 16383, /* stacksize */
- 20, /* priority */
- 0, /* timeslice */
- 0, /* block */
- NULL,0,0); /* not used */
- efs_clone(epmd_);
- start(epmd_);
- send(&sig, epmd_);
-#ifdef DEBUG
- printf("EPMD ID: %li\n", epmd_);
-#endif
- }
- return 0;
-}
-
-OS_PROCESS(ose_epmd) {
- union SIGNAL *sig;
- static const SIGSELECT rec_any_sig[] = { 0 };
- int i, argc;
- char **argv;
-
- sig = receive((SIGSELECT*)rec_any_sig);
-
- argc = sig->args.argc;
- argv = (char **)malloc((argc+1)*sizeof(char *));
- for(i=0; i<argc; i++) {
- argv[i] = (char *)malloc(strlen((sig->args.argv)[i])+1);
- strcpy(argv[i], (sig->args.argv)[i]);
- }
- argv[argc] = NULL;
- free_buf(&sig);
-
- epmd(argc, argv);
-
- for(i=0; i<argc; i++) {
- free(argv[i]);
- }
- free(argv);
-}
-
-#else /* ifdef _OSE_ */
-
-/* VxWorks start function */
+#ifdef VXWORKS
int start_epmd(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)
char *a0, *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8, *a9;
{
@@ -200,10 +118,7 @@ char *a0, *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8, *a9;
argc,(int) argv,1,
0,0,0,0,0,0,0);
}
-
-#endif /* _OSE_ */
-
-
+#endif /* WxWorks */
int epmd(int argc, char **argv)
@@ -248,6 +163,7 @@ int main(int argc, char** argv)
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;
@@ -283,6 +199,9 @@ 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);
@@ -313,11 +232,17 @@ int main(int argc, char** argv)
else
usage(g);
epmd_cleanup_exit(g,0);
+ } else if (strcmp(argv[0], "-stop") == 0) {
+ if (argc == 2)
+ stop_cli(g, argv[1]);
+ else
+ usage(g);
+ epmd_cleanup_exit(g,0);
}
else
usage(g);
}
- dbg_printf(g,0,"epmd running - daemon = %d",g->is_daemon);
+ dbg_printf(g,1,"epmd running - daemon = %d",g->is_daemon);
#ifndef NO_SYSCONF
if ((g->max_conn = sysconf(_SC_OPEN_MAX)) <= 0)
@@ -453,7 +378,7 @@ static void run_daemon(EpmdVars *g)
}
#endif
-#if (defined(VXWORKS) || defined(_OSE_))
+#if defined(VXWORKS)
static void run_daemon(EpmdVars *g)
{
run(g);
@@ -469,9 +394,10 @@ 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]\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, " [-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, " -port No\n");
fprintf(stderr, " Let epmd listen to another port than default %d\n",
EPMD_PORT_NO);
@@ -481,8 +407,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");
@@ -494,6 +428,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 runniing 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);
}
@@ -513,20 +459,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
@@ -537,9 +483,10 @@ 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);
+ erts_vsnprintf(buf + len, DEBUG_BUFFER_SIZE - len, format, args);
if (perr == 1)
perror(buf);
else
@@ -626,4 +573,9 @@ 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 c12f711bc5..7c60ba0420 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,47 @@ 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");
+ epmd_cleanup_exit(g,1);
+ } else { /* rval is now 0 or 1 */
+ buf[rval] = '\0';
+ printf("epmd: local epmd responded with <%s>\n", buf);
+ epmd_cleanup_exit(g,1);
+ }
+}
+
+void stop_cli(EpmdVars *g, char *name)
+{
+ char buf[1024];
+ int fd, rval, bsize;
+
+ bsize = strlen(name);
+ if (bsize > 1000) {
+ printf("epmd: Name too long!");
+ epmd_cleanup_exit(g, 1);
+ }
+
+ fd = conn_to_epmd(g);
+ bsize++;
+ put_int16(bsize, buf);
+ buf[2] = EPMD_STOP_REQ;
+ bsize += 2;
+ strcpy(buf+3, name);
+
+ if (write(fd, buf, bsize) != bsize) {
+ printf("epmd: Can't write to epmd\n");
+ epmd_cleanup_exit(g,1);
+ }
+ if ((rval = read_fill(fd,buf,7)) == 7) {
+ buf[7] = '\000';
+ printf("%s\n", buf);
epmd_cleanup_exit(g,0);
} else if (rval < 0) {
printf("epmd: failed to read answer from local epmd\n");
diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h
index 65fcf9bacb..c2558d52a1 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%
*
@@ -37,19 +38,6 @@
#define DONT_USE_MAIN
#endif
-#ifdef _OSE_
-#define NO_SYSLOG
-#define NO_SYSCONF
-#define NO_DAEMON
-#define DONT_USE_MAIN
-#ifndef HAVE_SYS_TIME_H
-#define HAVE_SYS_TIME_H
-#endif
-#ifndef HAVE_UNISTD_H
-#define HAVE_UNISTD_H
-#endif
-#endif
-
/* ************************************************************************ */
/* Standard includes */
@@ -92,7 +80,7 @@
#endif
#endif /* ! VXWORKS */
-#if (!defined(__WIN32__) && !defined(_OSE_))
+#if !defined(__WIN32__)
# include <netinet/in.h>
# include <sys/socket.h>
# include <sys/stat.h>
@@ -105,10 +93,8 @@
# include <netinet/tcp.h>
#endif /* ! WIN32 */
-#ifndef _OSE_
#include <ctype.h>
#include <signal.h>
-#endif
#include <errno.h>
@@ -126,13 +112,6 @@
#include <stdarg.h>
-#ifdef _OSE_
-# include "ose.h"
-# include "inet.h"
-# include "sys/stat.h"
-#endif
-
-
/* ************************************************************************ */
/* Replace some functions by others by making the function name a macro */
@@ -148,10 +127,6 @@
#define sleep(n) taskDelay((n) * sysClkRateGet())
#endif /* VXWORKS */
-#ifdef _OSE_
-#define sleep(n) delay((n))
-#endif
-
#ifdef USE_BCOPY
# define memcpy(a, b, c) bcopy((b), (a), (c))
# define memcmp(a, b, c) bcmp((a), (b), (c))
@@ -167,6 +142,10 @@
# define EADDRINUSE WSAEADDRINUSE
#endif
+#if defined(__WIN32__) && !defined(ECONNABORTED)
+# define ECONNABORTED WSAECONNABORTED
+#endif
+
#ifndef SOMAXCONN
# define SOMAXCONN 128
#endif
@@ -192,26 +171,18 @@
#define FAMILY AF_INET6
#define SET_ADDR_LOOPBACK(addr, af, port) do { \
- static u_int32_t __addr[4] = IN6ADDR_LOOPBACK_INIT; \
memset((char*)&(addr), 0, sizeof(addr)); \
(addr).sin6_family = (af); \
(addr).sin6_flowinfo = 0; \
- (addr).sin6_addr.s6_addr32[0] = __addr[0]; \
- (addr).sin6_addr.s6_addr32[1] = __addr[1]; \
- (addr).sin6_addr.s6_addr32[2] = __addr[2]; \
- (addr).sin6_addr.s6_addr32[3] = __addr[3]; \
+ (addr).sin6_addr = in6addr_loopback; \
(addr).sin6_port = htons(port); \
} while(0)
#define SET_ADDR_ANY(addr, af, port) do { \
- static u_int32_t __addr[4] = IN6ADDR_ANY_INIT; \
memset((char*)&(addr), 0, sizeof(addr)); \
(addr).sin6_family = (af); \
(addr).sin6_flowinfo = 0; \
- (addr).sin6_addr.s6_addr32[0] = __addr[0]; \
- (addr).sin6_addr.s6_addr32[1] = __addr[1]; \
- (addr).sin6_addr.s6_addr32[2] = __addr[2]; \
- (addr).sin6_addr.s6_addr32[3] = __addr[3]; \
+ (addr).sin6_addr = in6addr_any; \
(addr).sin6_port = htons(port); \
} while(0)
@@ -275,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 */
@@ -316,6 +289,7 @@ typedef struct {
int debug;
int silent;
int is_daemon;
+ int brutal_kill;
unsigned packet_timeout;
unsigned delay_accept;
unsigned delay_write;
@@ -337,6 +311,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 a033fab244..ef471a473a 100644
--- a/erts/epmd/src/epmd_srv.c
+++ b/erts/epmd/src/epmd_srv.c
@@ -39,46 +39,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 +68,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);
@@ -138,7 +107,7 @@ void run(EpmdVars *g)
* because addresses will be reused even if they are still in use.
*/
-#if (!defined(__WIN32__) && !defined(_OSE_))
+#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);
@@ -156,10 +125,6 @@ void run(EpmdVars *g)
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. */
-#ifdef _OSE_
- opt = 1;
- if (ioctl(listensock, FIONBIO, (char*)&opt) != 0)
-#else
#if (defined(__WIN32__) || defined(NO_FCNTL))
opt = 1;
if (ioctl(listensock, FIONBIO, &opt) != 0) /* Gives warning in VxWorks */
@@ -167,7 +132,6 @@ void run(EpmdVars *g)
opt = fcntl(listensock, F_GETFL, 0);
if (fcntl(listensock, F_SETFL, opt | O_NONBLOCK) == -1)
#endif /* __WIN32__ || VXWORKS */
-#endif /* _OSE_ */
dbg_perror(g,"failed to set non-blocking mode of listening socket %d",
listensock);
@@ -176,18 +140,6 @@ void run(EpmdVars *g)
SET_ADDR_ANY(iserv_addr, FAMILY, sport);
}
-#ifdef _OSE_
- {
- int optlen = sizeof(opt);
- opt = 1;
- if(getsockopt(listensock, SOL_SOCKET, SO_REUSEADDR,
- (void*)&opt, &optlen) < 0)
- fprintf(stderr, "\n\nGETSOCKOPT FAILS! %d\n\n", errno);
- else if(opt == 1)
- fprintf(stderr, "SO_REUSEADDR is set!\n");
- }
-#endif
-
if(bind(listensock,(struct sockaddr*) &iserv_addr, sizeof(iserv_addr)) < 0 )
{
if (errno == EADDRINUSE)
@@ -227,8 +179,16 @@ void run(EpmdVars *g)
timeout.tv_sec = (g->packet_timeout < IDLE_TIMEOUT) ? 1 : IDLE_TIMEOUT;
timeout.tv_usec = 0;
- if ((ret = select(g->max_conn,&read_mask,(fd_set *)0,(fd_set *)0,&timeout)) < 0)
+ if ((ret = select(g->max_conn,&read_mask,(fd_set *)0,(fd_set *)0,&timeout)) < 0) {
dbg_perror(g,"error in select ");
+ switch (errno) {
+ case EAGAIN:
+ case EINTR:
+ break;
+ default:
+ epmd_cleanup_exit(g,1);
+ }
+ }
else {
time_t now;
if (ret == 0) {
@@ -312,11 +272,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;
}
@@ -401,12 +359,21 @@ static int do_accept(EpmdVars *g,int listensock)
if (msgsock < 0) {
dbg_perror(g,"error in accept");
- return EPMD_FALSE;
+ switch (errno) {
+ case EAGAIN:
+ case ECONNABORTED:
+ case EINTR:
+ return EPMD_FALSE;
+ default:
+ epmd_cleanup_exit(g,1);
+ }
}
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;
@@ -417,117 +384,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;
@@ -550,7 +423,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");
@@ -593,7 +476,7 @@ static void do_request(g, fd, s, buf, bsize)
if (bsize <= 1)
{
- dbg_printf(g,0,"packet to small for request PORT2_REQ (%d)", bsize);
+ dbg_printf(g,0,"packet too small for request PORT2_REQ (%d)", bsize);
return;
}
@@ -681,6 +564,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;
@@ -730,7 +617,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");
@@ -740,9 +639,18 @@ 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 to small for request STOP_REQ (%d)",bsize);
+ dbg_printf(g,0,"packet too small for request STOP_REQ (%d)",bsize);
return;
}
@@ -827,6 +735,14 @@ 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;
+#ifdef HAVE_SOCKLEN_T
+ socklen_t st;
+#else
+ int st;
+#endif
+ st = sizeof(si);
+
g->active_conn++;
s = &g->conn[i];
@@ -836,6 +752,20 @@ 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 regarder as non local host */
+ s->local_peer = EPMD_FALSE;
+ } else {
+ s->local_peer =
+ ((((unsigned) ntohl(si.sin_addr.s_addr)) & 0xFF000000U) ==
+ 0x7F000000U); /* Only 127.x.x.x allowed, no false positives */
+ }
+ 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 */
@@ -904,7 +834,7 @@ static void node_init(EpmdVars *g)
/* We have got a close on a connection and it may be a
- EPMD_ALIVE_CLOSE_REQ. Note that this call shouild be called
+ EPMD_ALIVE_CLOSE_REQ. Note that this call should be called
*before* calling conn_close() */
static int node_unreg(EpmdVars *g,char *name)
@@ -992,11 +922,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,
@@ -1022,6 +947,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 */
diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl
index 91e09faf75..da69412e12 100644
--- a/erts/epmd/test/epmd_SUITE.erl
+++ b/erts/epmd/test/epmd_SUITE.erl
@@ -63,7 +63,13 @@
alive_req_too_large/1,
returns_valid_empty_extra/1,
- returns_valid_populated_extra_with_nulls/1
+ returns_valid_populated_extra_with_nulls/1,
+ buffer_overrun/1,
+ buffer_overrun_1/1,
+ buffer_overrun_2/1,
+ no_nonlocal_register/1,
+ no_nonlocal_kill/1,
+ no_live_killing/1
]).
@@ -82,11 +88,8 @@
-define(REG_REPEAT_LIM,1000).
% Message codes in epmd protocol
--define(EPMD_ALIVE_REQ, $a).
-define(EPMD_ALIVE2_REQ, $x).
--define(EPMD_ALIVE_OK_RESP, $Y).
-define(EPMD_ALIVE2_RESP, $y).
--define(EPMD_PORT_REQ, $p).
-define(EPMD_PORT_PLEASE2_REQ, $z).
-define(EPMD_PORT2_RESP, $w).
-define(EPMD_NAMES_REQ, $n).
@@ -124,7 +127,15 @@ all(suite) ->
alive_req_too_large,
returns_valid_empty_extra,
- returns_valid_populated_extra_with_nulls
+ returns_valid_populated_extra_with_nulls,
+
+ buffer_overrun,
+ %buffer_overrun_1,
+ %buffer_overrun_2,
+
+ no_nonlocal_register,
+ no_nonlocal_kill,
+ no_live_killing
].
%%
@@ -132,7 +143,7 @@ all(suite) ->
%%
init_per_testcase(_Func, Config) ->
- Dog = test_server:timetrap(?SHORT_TEST_TIMEOUT),
+ Dog = test_server:timetrap(?MEDIUM_TEST_TIMEOUT),
cleanup(),
[{watchdog, Dog} | Config].
@@ -148,7 +159,7 @@ register_name(doc) ->
["Register a name"];
register_name(suite) ->
[];
-register_name(Config) when list(Config) ->
+register_name(Config) when is_list(Config) ->
?line ok = epmdrun(),
?line {ok,Sock} = register_node("foobar"),
?line ok = close(Sock), % Unregister
@@ -158,7 +169,7 @@ register_names_1(doc) ->
["Register and unregister two nodes"];
register_names_1(suite) ->
[];
-register_names_1(Config) when list(Config) ->
+register_names_1(Config) when is_list(Config) ->
?line ok = epmdrun(),
?line {ok,Sock1} = register_node("foobar"),
?line {ok,Sock2} = register_node("foozap"),
@@ -170,7 +181,7 @@ register_names_2(doc) ->
["Register and unregister two nodes"];
register_names_2(suite) ->
[];
-register_names_2(Config) when list(Config) ->
+register_names_2(Config) when is_list(Config) ->
?line ok = epmdrun(),
?line {ok,Sock1} = register_node("foobar"),
?line {ok,Sock2} = register_node("foozap"),
@@ -182,7 +193,7 @@ register_duplicate_name(doc) ->
["Two nodes with the same name"];
register_duplicate_name(suite) ->
[];
-register_duplicate_name(Config) when list(Config) ->
+register_duplicate_name(Config) when is_list(Config) ->
?line ok = epmdrun(),
?line {ok,Sock} = register_node("foobar"),
?line error = register_node("foobar"),
@@ -192,22 +203,9 @@ register_duplicate_name(Config) when list(Config) ->
% Internal function to register a node name, no close, i.e. unregister
register_node(Name) ->
- register_node(Name,?DUMMY_PORT).
-
-register_node(Name, Port) ->
- case send_req([?EPMD_ALIVE_REQ, put16(Port), Name]) of
- {ok,Sock} ->
- case recv(Sock,3) of
- {ok, [?EPMD_ALIVE_OK_RESP,_D1,_D0]} ->
- {ok,Sock};
- Other ->
- test_server:format("recv on sock ~w: ~p~n",
- [Sock,Other]),
- error
- end;
- error ->
- error
- end.
+ register_node_v2(?DUMMY_PORT,$M,0,5,5,Name,"").
+register_node(Name,Port) ->
+ register_node_v2(Port,$M,0,5,5,Name,"").
register_node_v2(Port, NodeType, Prot, HVsn, LVsn, Name, Extra) ->
Req = [?EPMD_ALIVE2_REQ, put16(Port), NodeType, Prot,
@@ -254,7 +252,7 @@ parse_port2_resp(Resp) ->
hvsn=HVsn,lvsn=LVsn,
node_name=binary_to_list(NodeName),
extra=binary_to_list(Extra)}};
- Other ->
+ _Other ->
test_server:format("invalid port2 resp: ~p~n",
[Resp]),
error
@@ -266,7 +264,7 @@ name_with_null_inside(doc) ->
["Register a name with a null char in it"];
name_with_null_inside(suite) ->
[];
-name_with_null_inside(Config) when list(Config) ->
+name_with_null_inside(Config) when is_list(Config) ->
?line ok = epmdrun(),
?line error = register_node("foo\000bar"),
ok.
@@ -277,11 +275,9 @@ name_null_terminated(doc) ->
["Register a name with terminating null byte"];
name_null_terminated(suite) ->
[];
-name_null_terminated(Config) when list(Config) ->
+name_null_terminated(Config) when is_list(Config) ->
?line ok = epmdrun(),
- ?line {ok,Sock} = register_node("foobar\000"),
- ?line error = register_node("foobar"),
- ?line ok = close(Sock), % Unregister
+ ?line error = register_node("foobar\000"),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -290,7 +286,7 @@ stupid_names_req(doc) ->
["Read names from epmd in a stupid way"];
stupid_names_req(suite) ->
[];
-stupid_names_req(Config) when list(Config) ->
+stupid_names_req(Config) when is_list(Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
LongDog = test_server:timetrap(?MEDIUM_TEST_TIMEOUT),
@@ -394,15 +390,15 @@ get_port_nr(doc) ->
["Register a name on a port and ask about port nr"];
get_port_nr(suite) ->
[];
-get_port_nr(Config) when list(Config) ->
- port_request([?EPMD_PORT_REQ,"foo"]).
+get_port_nr(Config) when is_list(Config) ->
+ port_request([?EPMD_PORT_PLEASE2_REQ,"foo"]).
slow_get_port_nr(doc) ->
["Register with slow write and ask about port nr"];
slow_get_port_nr(suite) ->
[];
-slow_get_port_nr(Config) when list(Config) ->
- port_request([?EPMD_PORT_REQ,d,$f,d,$o,d,$o]).
+slow_get_port_nr(Config) when is_list(Config) ->
+ port_request([?EPMD_PORT_PLEASE2_REQ,d,$f,d,$o,d,$o]).
% Internal function used above
@@ -413,9 +409,18 @@ port_request(M) ->
?line {ok,RSock} = register_node("foo", Port),
?line {ok,Sock} = connect(),
?line ok = send(Sock,[size16(M),M]),
- R = put16(Port),
- ?line {ok,R} = recv(Sock, length(R)),
- ?line ok = close(RSock),
+ ?line case recv_until_sock_closes(Sock) of
+ {ok, Resp} ->
+ ?line close(RSock),
+ ?line {ok,Rec} = parse_port2_resp(Resp),
+ ?line Port = Rec#node_info.port,
+ ok;
+ Other ->
+ ?line close(RSock),
+ ?line test_server:format("recv on sock ~w: ~p~n",
+ [Sock,Other]),
+ ?line throw({error,Other})
+ end,
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -424,8 +429,8 @@ unregister_others_name_1(doc) ->
["Unregister name of other node"];
unregister_others_name_1(suite) ->
[];
-unregister_others_name_1(Config) when list(Config) ->
- ?line ok = epmdrun(),
+unregister_others_name_1(Config) when is_list(Config) ->
+ ?line ok = epmdrun("-relaxed_command_check"),
?line {ok,RSock} = register_node("foo"),
?line {ok,Sock} = connect(),
M = [?EPMD_STOP_REQ,"foo"],
@@ -441,8 +446,8 @@ unregister_others_name_2(doc) ->
["Unregister name of other node"];
unregister_others_name_2(suite) ->
[];
-unregister_others_name_2(Config) when list(Config) ->
- ?line ok = epmdrun(),
+unregister_others_name_2(Config) when is_list(Config) ->
+ ?line ok = epmdrun("-relaxed_command_check"),
?line {ok,Sock} = connect(),
M = [?EPMD_STOP_REQ,"xxx42"],
?line ok = send(Sock,[size16(M),M]),
@@ -456,7 +461,7 @@ register_overflow(doc) ->
["Register too many, clean and redo 10 times"];
register_overflow(suite) ->
[];
-register_overflow(Config) when list(Config) ->
+register_overflow(Config) when is_list(Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
LongDog = test_server:timetrap(?LONG_TEST_TIMEOUT),
@@ -546,7 +551,7 @@ no_data(doc) ->
["Open but send no data"];
no_data(suite) ->
[];
-no_data(Config) when list(Config) ->
+no_data(Config) when is_list(Config) ->
?line ok = epmdrun(),
?line {ok,Sock} = connect(),
sleep(?LONG_PAUSE),
@@ -559,7 +564,7 @@ one_byte(doc) ->
["Send one byte only"];
one_byte(suite) ->
[];
-one_byte(Config) when list(Config) ->
+one_byte(Config) when is_list(Config) ->
?line ok = epmdrun(),
?line {ok,Sock} = connect(),
?line ok = send(Sock,[0]),
@@ -573,7 +578,7 @@ two_bytes(doc) ->
["Send packet size only"];
two_bytes(suite) ->
[];
-two_bytes(Config) when list(Config) ->
+two_bytes(Config) when is_list(Config) ->
?line ok = epmdrun(),
?line {ok,Sock} = connect(),
?line ok = send(Sock,[put16(3)]),
@@ -587,7 +592,7 @@ partial_packet(doc) ->
["Got only part of a packet"];
partial_packet(suite) ->
[];
-partial_packet(Config) when list(Config) ->
+partial_packet(Config) when is_list(Config) ->
?line ok = epmdrun(),
?line {ok,Sock} = connect(),
?line ok = send(Sock,[put16(100),"only a few bytes"]),
@@ -601,7 +606,7 @@ zero_length(doc) ->
["Invalid zero packet size"];
zero_length(suite) ->
[];
-zero_length(Config) when list(Config) ->
+zero_length(Config) when is_list(Config) ->
?line ok = epmdrun(),
?line {ok,Sock} = connect(),
?line ok = send(Sock,[0,0,0,0,0,0,0,0,0,0]),
@@ -615,15 +620,20 @@ too_large(doc) ->
["Invalid large packet"];
too_large(suite) ->
[];
-too_large(Config) when list(Config) ->
+too_large(Config) when is_list(Config) ->
?line ok = epmdrun(),
?line {ok,Sock} = connect(),
Size = 63000,
M = lists:duplicate(Size, $z),
?line ok = send(Sock,[put16(Size),M]),
sleep(?MEDIUM_PAUSE),
- ?line closed = recv(Sock,1),
- ok.
+ % With such a large packet, even the writes can fail as the
+ % daemon closes before everything is delivered -> econnaborted
+ case recv(Sock,1) of
+ closed -> ok;
+ {error,econnaborted} -> ok;
+ Other -> exit({unexpected,Other})
+ end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -631,10 +641,11 @@ alive_req_too_small_1(doc) ->
["Try to register but not enough data"];
alive_req_too_small_1(suite) ->
[];
-alive_req_too_small_1(Config) when list(Config) ->
+alive_req_too_small_1(Config) when is_list(Config) ->
?line ok = epmdrun(),
?line {ok,Sock} = connect(),
- M = [?EPMD_ALIVE_REQ, 42],
+ M = [?EPMD_ALIVE2_REQ, put16(?DUMMY_PORT),$M,0, put16(5),
+ put16(5),put16(0)],
?line ok = send(Sock, [size16(M), M]),
sleep(?MEDIUM_PAUSE),
?line closed = recv(Sock,1),
@@ -646,10 +657,11 @@ alive_req_too_small_2(doc) ->
["Try to register but not enough data"];
alive_req_too_small_2(suite) ->
[];
-alive_req_too_small_2(Config) when list(Config) ->
+alive_req_too_small_2(Config) when is_list(Config) ->
?line ok = epmdrun(),
?line {ok,Sock} = connect(),
- M = [?EPMD_ALIVE_REQ, put16(?DUMMY_PORT)],
+ M = [?EPMD_ALIVE2_REQ, put16(?DUMMY_PORT),$M,0, put16(5),
+ put16(5)],
?line ok = send(Sock, [size16(M), M]),
sleep(?MEDIUM_PAUSE),
?line closed = recv(Sock,1),
@@ -661,7 +673,7 @@ alive_req_too_large(doc) ->
["Try to register but node name too large"];
alive_req_too_large(suite) ->
[];
-alive_req_too_large(Config) when list(Config) ->
+alive_req_too_large(Config) when is_list(Config) ->
?line ok = epmdrun(),
?line {ok,Sock} = connect(),
L = [
@@ -678,10 +690,12 @@ alive_req_too_large(Config) when list(Config) ->
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
],
- M = [?EPMD_ALIVE_REQ, put16(?DUMMY_PORT), L],
+ S = length(lists:flatten(L)),
+ M = [?EPMD_ALIVE2_REQ, put16(?DUMMY_PORT),$M,0, put16(5),
+ put16(5), put16(S),L,put16(0)],
?line ok = send(Sock, [size16(M), M]),
sleep(?MEDIUM_PAUSE),
- ?line closed = recv(Sock,1),
+ ?line {ok,[?EPMD_ALIVE2_RESP,1]} = recv(Sock,2),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -690,7 +704,7 @@ returns_valid_empty_extra(doc) ->
["Check that an empty extra is prefixed by a two byte length"];
returns_valid_empty_extra(suite) ->
[];
-returns_valid_empty_extra(Config) when list(Config) ->
+returns_valid_empty_extra(Config) when is_list(Config) ->
?line ok = epmdrun(),
?line {ok,Sock} = register_node_v2(4711, 72, 0, 5, 5, "foo", []),
?line {ok,#node_info{extra=[]}} = port_please_v2("foo"),
@@ -703,7 +717,7 @@ returns_valid_populated_extra_with_nulls(doc) ->
["Check a populated extra with embedded null characters"];
returns_valid_populated_extra_with_nulls(suite) ->
[];
-returns_valid_populated_extra_with_nulls(Config) when list(Config) ->
+returns_valid_populated_extra_with_nulls(Config) when is_list(Config) ->
?line ok = epmdrun(),
?line {ok,Sock} = register_node_v2(4711, 72, 0, 5, 5, "foo", "ABC\000\000"),
?line {ok,#node_info{extra="ABC\000\000"}} = port_please_v2("foo"),
@@ -711,6 +725,167 @@ returns_valid_populated_extra_with_nulls(Config) when list(Config) ->
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+buffer_overrun(suite) ->
+ [buffer_overrun_1,buffer_overrun_2].
+
+buffer_overrun_1(suite) ->
+ [];
+buffer_overrun_1(doc) ->
+ ["Test security vulnerability in fake extra lengths in alive2_req"];
+buffer_overrun_1(Config) when is_list(Config) ->
+ ?line ok = epmdrun(),
+ ?line true = alltrue([hostile(N) || N <- lists:seq(1,10000)]),
+ ok.
+buffer_overrun_2(suite) ->
+ [];
+buffer_overrun_2(doc) ->
+ ["Test security vulnerability in fake extra lengths in alive2_req"];
+buffer_overrun_2(Config) when is_list(Config) ->
+ ?line ok = epmdrun(),
+ ?line [false | Rest] = [hostile2(N) || N <- lists:seq(255,10000)],
+ ?line true = alltrue(Rest),
+ ok.
+hostile(N) ->
+ try
+ Bin= <<$x:8,4747:16,$M:8,0:8,5:16,5:16,5:16,"gurka",N:16>>,
+ S = size(Bin),
+ {ok,E}=connect_sturdy(),
+ gen_tcp:send(E,[<<S:16>>,Bin]),
+ closed = recv(E,1),
+ gen_tcp:close(E),
+ true
+ catch
+ _:_ ->
+ false
+ end.
+hostile2(N) ->
+ try
+ B2 = list_to_binary(lists:duplicate(N,255)),
+ Bin= <<$x:8,4747:16,$M:8,0:8,5:16,5:16,5:16,"gurka",N:16,B2/binary>>,
+ S = size(Bin),
+ {ok,E}=connect_sturdy(),
+ gen_tcp:send(E,[<<S:16>>,Bin]),
+ Z = recv(E,2),
+ gen_tcp:close(E),
+ (Z =:= closed) or (Z =:= {ok, [$y,1]})
+ catch
+ _A:_B ->
+ false
+ end.
+
+alltrue([]) ->
+ true;
+alltrue([true|T]) ->
+ alltrue(T);
+alltrue([_|_]) ->
+ false.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+no_nonlocal_register(suite) ->
+ [];
+no_nonlocal_register(doc) ->
+ ["Ensure that we cannot register throug a nonlocal connection"];
+no_nonlocal_register(Config) when is_list(Config) ->
+ ?line ok = epmdrun(),
+ ?line {ok,Ifs} = inet:getiflist(),
+ ?line Addr0 = [ inet:ifget(I, [addr]) || I <- Ifs ],
+ ?line Addr1 = [ A || {ok,[{addr,A}]} <- Addr0],
+ ?line Addr = lists:filter(fun({127,_,_,_}) ->
+ false;
+ (_) ->
+ true
+ end,Addr1),
+ %% Now we should have all non loopback interface addresses,
+ %% none should accept a alive2 registration.
+ ?line Res = lists:map(fun(Ad={A1,A2,A3,A4}) ->
+ try
+ Name = "gurka_"++
+ integer_to_list(A1)++"_"++
+ integer_to_list(A2)++"_"++
+ integer_to_list(A3)++"_"++
+ integer_to_list(A4),
+ Bname = list_to_binary(Name),
+ NameS = byte_size(Bname),
+ ?line Bin= <<$x:8,4747:16,$M:8,0:8,5:16,
+ 5:16,NameS:16,Bname/binary,
+ 0:16>>,
+ ?line S = size(Bin),
+ {ok, E} = connect(Ad),
+ gen_tcp:send(E,[<<S:16>>,Bin]),
+ closed = recv(E,1),
+ gen_tcp:close(E),
+ true
+ catch
+ _:_ ->
+ false
+ end
+ end, Addr),
+ erlang:display(Res),
+ ?line true = alltrue(Res),
+ ok.
+
+no_nonlocal_kill(suite) ->
+ [];
+no_nonlocal_kill(doc) ->
+ ["Ensure that we cannot kill through nonlocal connection"];
+no_nonlocal_kill(Config) when is_list(Config) ->
+ ?line ok = epmdrun(),
+ ?line {ok,Ifs} = inet:getiflist(),
+ ?line Addr0 = [ inet:ifget(I, [addr]) || I <- Ifs ],
+ ?line Addr1 = [ A || {ok,[{addr,A}]} <- Addr0],
+ ?line Addr = lists:filter(fun({127,_,_,_}) ->
+ false;
+ (_) ->
+ true
+ end,Addr1),
+ %% Now we should have all non loopback interface addresses,
+ %% none should accept a alive2 registration.
+ ?line Res = lists:map(fun(Ad) ->
+ try
+ {ok, E} = connect(Ad),
+ M = [?EPMD_KILL_REQ],
+ send(E, [size16(M), M]),
+ closed = recv(E,2),
+ gen_tcp:close(E),
+ sleep(?MEDIUM_PAUSE),
+ {ok, E2} = connect(Ad),
+ gen_tcp:close(E2),
+ true
+ catch
+ _:_ ->
+ false
+ end
+ end, Addr),
+ erlang:display(Res),
+ ?line true = alltrue(Res),
+ ok.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+no_live_killing(doc) ->
+ ["Dont allow killing with live nodes or any unregistering w/o -relaxed_command_check"];
+no_live_killing(suite) ->
+ [];
+no_live_killing(Config) when is_list(Config) ->
+ ?line ok = epmdrun(),
+ ?line {ok,RSock} = register_node("foo"),
+ ?line {ok,Sock} = connect(),
+ ?line M = [?EPMD_KILL_REQ],
+ ?line ok = send(Sock,[size16(M),M]),
+ ?line {ok,"NO"} = recv(Sock,2),
+ ?line close(Sock),
+ ?line {ok,Sock2} = connect(),
+ ?line M2 = [?EPMD_STOP_REQ,"foo"],
+ ?line ok = send(Sock2,[size16(M2),M2]),
+ ?line closed = recv(Sock2,1),
+ ?line close(Sock2),
+ ?line close(RSock),
+ ?line sleep(?MEDIUM_PAUSE),
+ ?line {ok,Sock3} = connect(),
+ ?line M3 = [?EPMD_KILL_REQ],
+ ?line ok = send(Sock3,[size16(M3),M3]),
+ ?line {ok,"OK"} = recv(Sock3,2),
+ ?line close(Sock3),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Terminate all tests with killing epmd.
cleanup() ->
@@ -731,16 +906,24 @@ cleanup() ->
% Normal debug start of epmd
epmdrun() ->
+ epmdrun([]).
+epmdrun(Args) ->
case os:find_executable(epmd) of
false ->
{error, {could_not_find_epmd_in_path}};
Path ->
- epmdrun(Path)
+ epmdrun(Path,Args)
end.
-epmdrun(Epmd) ->
+epmdrun(Epmd,Args0) ->
%% test_server:format("epmdrun() => Epmd = ~p",[Epmd]),
- osrun(Epmd ++ " " ?EPMDARGS " -port " ++ integer_to_list(?PORT)).
+ Args = case Args0 of
+ [] ->
+ [];
+ O ->
+ " "++O
+ end,
+ osrun(Epmd ++ Args ++ " " ?EPMDARGS " -port " ++ integer_to_list(?PORT)).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -753,20 +936,27 @@ osrun(Cmd) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Wrappers of TCP functions
-% These two functions is the interface for connect.
+% These functions is the interface for connect.
% Passive mode is the default
connect() ->
- connect(?PORT, passive).
+ connect("localhost",?PORT, passive).
+
+connect(Addr) ->
+ connect(Addr,?PORT, passive).
connect_active() ->
- connect(?PORT, active).
+ connect("localhost",?PORT, active).
+%% Retry after 15 seconds, to avoid TIME_WAIT socket exhaust.
+connect_sturdy() ->
+ connect("localhost",?PORT, passive, 15000, 3).
% Try a few times before giving up
-
-connect(Port, Mode) ->
- case connect_repeat(?CONN_RETRY, Port, Mode) of
+connect(Addr, Port, Mode) ->
+ connect(Addr, Port, Mode, ?CONN_SLEEP, ?CONN_RETRY).
+connect(Addr, Port, Mode, Sleep, Retry) ->
+ case connect_repeat(Addr, Retry, Port, Mode, Sleep) of
{ok,Sock} ->
{ok,Sock};
{error,timeout} ->
@@ -783,25 +973,25 @@ connect(Port, Mode) ->
% Try a few times before giving up. Pause a small time between
% each try.
-connect_repeat(1, Port, Mode) ->
- connect_mode(Port, Mode);
-connect_repeat(Retry, Port, Mode) ->
- case connect_mode(Port, Mode) of
+connect_repeat(Addr, 1, Port, Mode, _Sleep) ->
+ connect_mode(Addr,Port, Mode);
+connect_repeat(Addr,Retry, Port, Mode, Sleep) ->
+ case connect_mode(Addr,Port, Mode) of
{ok,Sock} ->
{ok,Sock};
{error,Reason} ->
test_server:format("connect: error: ~w~n",[Reason]),
- timer:sleep(?CONN_SLEEP),
- connect_repeat(Retry - 1, Port, Mode);
+ timer:sleep(Sleep),
+ connect_repeat(Addr, Retry - 1, Port, Mode, Sleep);
Any ->
test_server:format("connect: unknown message: ~w~n",[Any]),
exit(1)
end.
-connect_mode(Port, active) ->
- gen_tcp:connect("localhost", Port, [{packet, 0}], ?CONN_TIMEOUT);
-connect_mode(Port, passive) ->
- gen_tcp:connect("localhost", Port, [{packet, 0}, {active, false}],
+connect_mode(Addr,Port, active) ->
+ gen_tcp:connect(Addr, Port, [{packet, 0}], ?CONN_TIMEOUT);
+connect_mode(Addr, Port, passive) ->
+ gen_tcp:connect(Addr, Port, [{packet, 0}, {active, false}],
?CONN_TIMEOUT).
@@ -858,9 +1048,9 @@ send(Sock, SendSpec) ->
send([], RevBytes, _Sock) ->
{ok,RevBytes};
-send([Byte | Spec], RevBytes, Sock) when integer(Byte) ->
+send([Byte | Spec], RevBytes, Sock) when is_integer(Byte) ->
send(Spec, [Byte | RevBytes], Sock);
-send([List | Spec], RevBytes, Sock) when list(List) ->
+send([List | Spec], RevBytes, Sock) when is_list(List) ->
case send(List, RevBytes, Sock) of
{ok,Left} ->
send(Spec, Left, Sock);