diff options
Diffstat (limited to 'erts/epmd/src')
-rw-r--r-- | erts/epmd/src/Makefile.in | 25 | ||||
-rw-r--r-- | erts/epmd/src/epmd.c | 53 | ||||
-rw-r--r-- | erts/epmd/src/epmd.h | 21 | ||||
-rw-r--r-- | erts/epmd/src/epmd_cli.c | 8 | ||||
-rw-r--r-- | erts/epmd/src/epmd_int.h | 9 | ||||
-rw-r--r-- | erts/epmd/src/epmd_srv.c | 212 |
6 files changed, 156 insertions, 172 deletions
diff --git a/erts/epmd/src/Makefile.in b/erts/epmd/src/Makefile.in index 498756b468..70a54fe46e 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,27 @@ 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 +ERTS_INTERNAL_LIBS=-L../../lib/internal/$(TARGET) -lerts_internal$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@ -lm +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 +102,7 @@ EPMD_OBJS = $(OBJDIR)/epmd.o \ #--------------------------------- -all: $(BINDIR)/$(EPMD) +all: erts_lib $(BINDIR)/$(EPMD) docs: @@ -109,9 +121,12 @@ clean: $(BINDIR)/$(EPMD): $(EPMD_OBJS) $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(EPMD_OBJS) $(LIBS) -$(OBJDIR)/%.o: %.c +$(OBJDIR)/%.o: %.c epmd.h epmd_int.h $(CC) $(CFLAGS) $(EPMD_FLAGS) -o $@ -c $< +erts_lib: + cd $(ERL_TOP)/erts/lib_src && $(MAKE) $(TYPE) + include $(ERL_TOP)/make/otp_release_targets.mk release_spec: all diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c index 9c2ce065bb..65ff0cd6b2 100644 --- a/erts/epmd/src/epmd.c +++ b/erts/epmd/src/epmd.c @@ -23,6 +23,7 @@ #endif #include "epmd.h" /* Renamed from 'epmd_r4.h' */ #include "epmd_int.h" +#include "erl_printf.h" #ifdef HAVE_STDLIB_H # include <stdlib.h> @@ -33,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 @@ -161,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; @@ -196,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); @@ -388,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|-stop name]\n\n"); - fprintf(stderr, "See the Erlang epmd manual page for info about the usage.\n"); - fprintf(stderr, "The -port and DbgExtra options are\n\n"); + fprintf(stderr, " [-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); @@ -400,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"); @@ -413,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); } @@ -432,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 @@ -456,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 @@ -545,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 2aed861390..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,11 @@ void kill_epmd(EpmdVars *g) epmd_cleanup_exit(g,1); } if ((rval = read_fill(fd,buf,2)) == 2) { - printf("Killed\n"); + if (buf[0] == 'O' && buf[1] == 'K') { + printf("Killed\n"); + } else { + printf("Killing not allowed - living nodes in database.\n"); + } epmd_cleanup_exit(g,0); } else if (rval < 0) { printf("epmd: failed to read answer from local epmd\n"); diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h index 5ead553f36..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% * @@ -245,8 +246,10 @@ typedef struct { int fd; /* File descriptor */ - unsigned open:1; /* TRUE if open */ - unsigned keep:1; /* Don't close when sent reply */ + unsigned char open; /* TRUE if open */ + unsigned char keep; /* Don't close when sent reply */ + unsigned char local_peer; /* The peer of this connection is via + loopback interface */ unsigned got; /* # of bytes we have got */ unsigned want; /* Number of bytes we want */ char *buf; /* The remaining buffer */ @@ -286,6 +289,7 @@ typedef struct { int debug; int silent; int is_daemon; + int brutal_kill; unsigned packet_timeout; unsigned delay_accept; unsigned delay_write; @@ -307,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 c836bf0bb7..df4d1a5715 100644 --- a/erts/epmd/src/epmd_srv.c +++ b/erts/epmd/src/epmd_srv.c @@ -47,38 +47,6 @@ * | 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 +66,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); @@ -303,11 +270,9 @@ static void do_read(EpmdVars *g,Connection *s) s->fd,val); dbg_print_buf(g,s->buf,val); - /* FIXME: Shouldn't be needed to close down.... */ node_unreg_sock(g,s->fd); epmd_conn_close(g,s); } - /* FIXME: We always close, probably the right thing to do */ return; } @@ -405,6 +370,8 @@ static int do_accept(EpmdVars *g,int listensock) return conn_open(g,msgsock); } +/* buf is actually one byte larger than bsize, + giving place for null termination */ static void do_request(g, fd, s, buf, bsize) EpmdVars *g; int fd; @@ -415,117 +382,23 @@ static void do_request(g, fd, s, buf, bsize) char wbuf[OUTBUF_SIZE]; /* Buffer for writing */ int i; - /* - * Terminate packet as a C string. Needed for requests received from Erlang - * nodes with lower version than R3A. - */ - - buf[bsize] = '\0'; + buf[bsize] = '\0'; /* Needed for strcmp in PORT2 and STOP requests + buf is always large enough */ switch (*buf) { - case EPMD_ALIVE_REQ: - dbg_printf(g,1,"** got ALIVE_REQ"); - - /* The packet has the format "axxyyyyyy" where xx is port, given - in network byte order, and yyyyyy is symname, possibly null - terminated. */ - - if (buf[bsize - 1] == '\000') /* Skip null termination */ - bsize--; - - if (bsize <= 3) - { - dbg_printf(g,0,"packet to small for request ALIVE_REQ (%d)", bsize); - return; - } - - for (i = 3; i < bsize; i++) - if (buf[i] == '\000') - { - dbg_printf(g,0,"node name contains ascii 0 in ALIVE_REQ"); - return; - } - - { - Node *node; - int eport; - char *name = &buf[3]; /* points to node name */ - - eport = get_int16(&buf[1]); - - if ((node = node_reg(g, name, fd, eport)) == NULL) - return; - - wbuf[0] = EPMD_ALIVE_OK_RESP; - put_int16(node->creation, wbuf+1); - - if (g->delay_write) /* Test of busy server */ - sleep(g->delay_write); - - if (reply(g, fd, wbuf, 3) != 3) - { - dbg_tty_printf(g,1,"failed to send ALIVE_OK_RESP for \"%s\"",name); - return; - } - - dbg_tty_printf(g,1,"** sent ALIVE_OK_RESP for \"%s\"",name); - s->keep = EPMD_TRUE; /* Don't close on inactivity */ - } - break; - - case EPMD_PORT_REQ: - dbg_printf(g,1,"** got PORT_REQ"); - - if (buf[bsize - 1] == '\000') /* Skip null termination */ - bsize--; - - if (bsize <= 1) - { - dbg_printf(g,0,"packet to small for request PORT_REQ (%d)", bsize); - return; - } - - for (i = 1; i < bsize; i++) - if (buf[i] == '\000') - { - dbg_printf(g,0,"node name contains ascii 0 in PORT_REQ"); - return; - } - - { - char *name = &buf[1]; /* Points to node name */ - Node *node; - - for (node = g->nodes.reg; node; node = node->next) - { - if (strcmp(node->symname, name) == 0) - { - put_int16(node->port,wbuf); - if (reply(g, fd, wbuf, 2) != 2) - { - dbg_tty_printf(g,1,"failed to send PORT_RESP for %s: %d", - name,node->port); - return; - } - dbg_tty_printf(g,1,"** sent PORT_RESP for %s: %d", - name,node->port); - return; - } - } - dbg_tty_printf(g,1,"Closed on PORT_REQ for %s",name); - } - /* FIXME: How about an answer if no port? Is a close enough? */ - break; - case EPMD_ALIVE2_REQ: dbg_printf(g,1,"** got ALIVE2_REQ"); + if (!s->local_peer) { + dbg_printf(g,0,"ALIVE2_REQ from non local address"); + return; + } /* The packet has the format "axxyyyyyy" where xx is port, given in network byte order, and yyyyyy is symname, possibly null terminated. */ - if (bsize <= 13) + if (bsize <= 13) /* at least one character for the node name */ { dbg_printf(g,0,"packet to small for request ALIVE2_REQ (%d)",bsize); return; @@ -548,7 +421,17 @@ static void do_request(g, fd, s, buf, bsize) highvsn = get_int16(&buf[5]); lowvsn = get_int16(&buf[7]); namelen = get_int16(&buf[9]); + if (namelen + 13 > bsize) { + dbg_printf(g,0,"Node name size error in ALIVE2_REQ"); + return; + } extralen = get_int16(&buf[11+namelen]); + + if (extralen + namelen + 13 > bsize) { + dbg_printf(g,0,"Extra info size error in ALIVE2_REQ"); + return; + } + for (i = 11 ; i < 11 + namelen; i ++) if (buf[i] == '\000') { dbg_printf(g,0,"node name contains ascii 0 in ALIVE2_REQ"); @@ -679,6 +562,10 @@ static void do_request(g, fd, s, buf, bsize) case EPMD_DUMP_REQ: dbg_printf(g,1,"** got DUMP_REQ"); + if (!s->local_peer) { + dbg_printf(g,0,"DUMP_REQ from non local address"); + return; + } { Node *node; @@ -728,7 +615,19 @@ static void do_request(g, fd, s, buf, bsize) break; case EPMD_KILL_REQ: + if (!s->local_peer) { + dbg_printf(g,0,"KILL_REQ from non local address"); + return; + } dbg_printf(g,1,"** got KILL_REQ"); + + if (!g->brutal_kill && (g->nodes.reg != NULL)) { + dbg_printf(g,0,"Disallowed KILL_REQ, live nodes"); + if (reply(g, fd,"NO",2) != 2) + dbg_printf(g,0,"failed to send reply to KILL_REQ"); + return; + } + if (reply(g, fd,"OK",2) != 2) dbg_printf(g,0,"failed to send reply to KILL_REQ"); dbg_tty_printf(g,1,"epmd killed"); @@ -738,6 +637,15 @@ static void do_request(g, fd, s, buf, bsize) case EPMD_STOP_REQ: dbg_printf(g,1,"** got STOP_REQ"); + if (!s->local_peer) { + dbg_printf(g,0,"STOP_REQ from non local address"); + return; + } + if (!g->brutal_kill) { + dbg_printf(g,0,"Disallowed STOP_REQ, no relaxed_command_check"); + return; + } + if (bsize <= 1 ) { dbg_printf(g,0,"packet too small for request STOP_REQ (%d)",bsize); @@ -825,6 +733,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]; @@ -834,6 +750,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 */ @@ -990,11 +920,6 @@ static int node_unreg_sock(EpmdVars *g,int fd) * Perhaps use the oldest or something. */ -static Node *node_reg(EpmdVars *g,char *name,int fd, int port) -{ - return node_reg2(g, name, fd, port, 0, 0, 0, 0, 0, NULL); -} - static Node *node_reg2(EpmdVars *g, char* name, int fd, @@ -1020,6 +945,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 */ |