diff options
Diffstat (limited to 'erts/epmd')
-rw-r--r-- | erts/epmd/src/Makefile.in | 29 | ||||
-rw-r--r-- | erts/epmd/src/epmd.c | 164 | ||||
-rw-r--r-- | erts/epmd/src/epmd.h | 21 | ||||
-rw-r--r-- | erts/epmd/src/epmd_cli.c | 44 | ||||
-rw-r--r-- | erts/epmd/src/epmd_int.h | 53 | ||||
-rw-r--r-- | erts/epmd/src/epmd_srv.c | 262 | ||||
-rw-r--r-- | erts/epmd/test/epmd_SUITE.erl | 356 |
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); |