diff options
Diffstat (limited to 'erts/epmd')
-rw-r--r-- | erts/epmd/src/epmd.c | 39 | ||||
-rw-r--r-- | erts/epmd/src/epmd_cli.c | 2 | ||||
-rw-r--r-- | erts/epmd/src/epmd_int.h | 59 | ||||
-rw-r--r-- | erts/epmd/src/epmd_srv.c | 224 | ||||
-rw-r--r-- | erts/epmd/test/Makefile | 2 | ||||
-rw-r--r-- | erts/epmd/test/epmd.spec | 2 | ||||
-rw-r--r-- | erts/epmd/test/epmd_SUITE.erl | 75 |
7 files changed, 246 insertions, 157 deletions
diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c index 47ee4f1150..a1f202251c 100644 --- a/erts/epmd/src/epmd.c +++ b/erts/epmd/src/epmd.c @@ -33,6 +33,7 @@ static void usage(EpmdVars *); static void run_daemon(EpmdVars*); +static char* get_addresses(void); static int get_port_no(void); static int check_relaxed(void); #ifdef __WIN32__ @@ -133,6 +134,7 @@ int main(int argc, char** argv) { EpmdVars g_empd_vars; EpmdVars *g = &g_empd_vars; + int i; #ifdef __WIN32__ WORD wVersionRequested; WSADATA wsaData; @@ -158,8 +160,9 @@ int main(int argc, char** argv) g->argv = NULL; #endif - g->port = get_port_no(); - g->debug = 0; + g->addresses = get_addresses(); + g->port = get_port_no(); + g->debug = 0; g->silent = 0; g->is_daemon = 0; @@ -168,12 +171,14 @@ int main(int argc, char** argv) g->delay_accept = 0; g->delay_write = 0; g->progname = argv[0]; - g->listenfd = -1; g->conn = NULL; g->nodes.reg = g->nodes.unreg = g->nodes.unreg_tail = NULL; g->nodes.unreg_count = 0; g->active_conn = 0; + for (i = 0; i < MAX_LISTEN_SOCKETS; i++) + g->listenfd[i] = -1; + argc--; argv++; while (argc > 0) { @@ -208,6 +213,11 @@ int main(int argc, char** argv) else usage(g); epmd_cleanup_exit(g,0); + } else if (strcmp(argv[0], "-address") == 0) { + if (argc == 1) + usage(g); + g->addresses = argv[1]; + argv += 2; argc -= 2; } else if (strcmp(argv[0], "-port") == 0) { if ((argc == 1) || ((g->port = atoi(argv[1])) == 0)) @@ -252,13 +262,10 @@ int main(int argc, char** argv) /* * max_conn must not be greater than FD_SETSIZE. * (at least QNX crashes) - * - * More correctly, it must be FD_SETSIZE - 1, beacuse the - * listen FD is stored outside the connection array. */ if (g->max_conn > FD_SETSIZE) { - g->max_conn = FD_SETSIZE - 1; + g->max_conn = FD_SETSIZE; } if (g->is_daemon) { @@ -393,11 +400,14 @@ static void run_daemon(EpmdVars *g) static void usage(EpmdVars *g) { - fprintf(stderr, "usage: epmd [-d|-debug] [DbgExtra...] [-port No] [-daemon]\n"); - fprintf(stderr, " [-relaxed_command_check]\n"); + fprintf(stderr, "usage: epmd [-d|-debug] [DbgExtra...] [-address List]\n"); + fprintf(stderr, " [-port No] [-daemon] [-relaxed_command_check]\n"); fprintf(stderr, " epmd [-d|-debug] [-port No] [-names|-kill|-stop name]\n\n"); fprintf(stderr, "See the Erlang epmd manual page for info about the usage.\n\n"); fprintf(stderr, "Regular options\n"); + fprintf(stderr, " -address List\n"); + fprintf(stderr, " Let epmd listen only on the comma-separated list of IP\n"); + fprintf(stderr, " addresses (and on the loopback interface).\n"); fprintf(stderr, " -port No\n"); fprintf(stderr, " Let epmd listen to another port than default %d\n", EPMD_PORT_NO); @@ -433,7 +443,7 @@ static void usage(EpmdVars *g) 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, " Kill the currently running epmd\n"); fprintf(stderr, " (only allowed if -names show empty database or\n"); fprintf(stderr, " -relaxed_command_check was given when epmd was started).\n"); fprintf(stderr, " -stop Name\n"); @@ -555,8 +565,9 @@ void epmd_cleanup_exit(EpmdVars *g, int exitval) epmd_conn_close(g,&g->conn[i]); free(g->conn); } - if(g->listenfd >= 0) - close(g->listenfd); + for(i=0; i < MAX_LISTEN_SOCKETS; i++) + if(g->listenfd[i] >= 0) + close(g->listenfd[i]); free_all_nodes(g); if(g->argv){ for(i=0; g->argv[i] != NULL; ++i) @@ -568,6 +579,10 @@ void epmd_cleanup_exit(EpmdVars *g, int exitval) exit(exitval); } +static char* get_addresses(void) +{ + return getenv("ERL_EPMD_ADDRESS"); +} static int get_port_no(void) { char* port_str = getenv("ERL_EPMD_PORT"); diff --git a/erts/epmd/src/epmd_cli.c b/erts/epmd/src/epmd_cli.c index 7c60ba0420..ac55ba6bb6 100644 --- a/erts/epmd/src/epmd_cli.c +++ b/erts/epmd/src/epmd_cli.c @@ -137,7 +137,7 @@ static int conn_to_epmd(EpmdVars *g) { /* store port number in unsigned short */ unsigned short sport = g->port; - SET_ADDR_LOOPBACK(address, FAMILY, sport); + SET_ADDR(address, EPMD_ADDR_LOOPBACK, sport); } if (connect(connect_sock, (struct sockaddr*)&address, sizeof address) < 0) diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h index c2558d52a1..2a0de4df9c 100644 --- a/erts/epmd/src/epmd_int.h +++ b/erts/epmd/src/epmd_int.h @@ -168,42 +168,40 @@ #if defined(HAVE_IN6) && defined(AF_INET6) && defined(EPMD6) #define EPMD_SOCKADDR_IN sockaddr_in6 -#define FAMILY AF_INET6 - -#define SET_ADDR_LOOPBACK(addr, af, port) do { \ - memset((char*)&(addr), 0, sizeof(addr)); \ - (addr).sin6_family = (af); \ - (addr).sin6_flowinfo = 0; \ - (addr).sin6_addr = in6addr_loopback; \ - (addr).sin6_port = htons(port); \ +#define EPMD_IN_ADDR in6_addr +#define EPMD_S_ADDR s6_addr +#define EPMD_ADDR_LOOPBACK in6addr_loopback.s6_addr +#define EPMD_ADDR_ANY in6addr_any.s6_addr +#define FAMILY AF_INET6 + +#define SET_ADDR(dst, addr, port) do { \ + memset((char*)&(dst), 0, sizeof(dst)); \ + memcpy((char*)&(dst).sin6_addr.s6_addr, (char*)&(addr), 16); \ + (dst).sin6_family = AF_INET6; \ + (dst).sin6_flowinfo = 0; \ + (dst).sin6_port = htons(port); \ } while(0) -#define SET_ADDR_ANY(addr, af, port) do { \ - memset((char*)&(addr), 0, sizeof(addr)); \ - (addr).sin6_family = (af); \ - (addr).sin6_flowinfo = 0; \ - (addr).sin6_addr = in6addr_any; \ - (addr).sin6_port = htons(port); \ - } while(0) +#define IS_ADDR_LOOPBACK(addr) \ + (memcmp((addr).s6_addr, in6addr_loopback.s6_addr, 16) == 0) #else /* Not IP v6 */ #define EPMD_SOCKADDR_IN sockaddr_in -#define FAMILY AF_INET - -#define SET_ADDR_LOOPBACK(addr, af, port) do { \ - memset((char*)&(addr), 0, sizeof(addr)); \ - (addr).sin_family = (af); \ - (addr).sin_addr.s_addr = htonl(INADDR_LOOPBACK); \ - (addr).sin_port = htons(port); \ +#define EPMD_IN_ADDR in_addr +#define EPMD_S_ADDR s_addr +#define EPMD_ADDR_LOOPBACK htonl(INADDR_LOOPBACK) +#define EPMD_ADDR_ANY htonl(INADDR_ANY) +#define FAMILY AF_INET + +#define SET_ADDR(dst, addr, port) do { \ + memset((char*)&(dst), 0, sizeof(dst)); \ + (dst).sin_family = AF_INET; \ + (dst).sin_addr.s_addr = (addr); \ + (dst).sin_port = htons(port); \ } while(0) -#define SET_ADDR_ANY(addr, af, port) do { \ - memset((char*)&(addr), 0, sizeof(addr)); \ - (addr).sin_family = (af); \ - (addr).sin_addr.s_addr = htonl(INADDR_ANY); \ - (addr).sin_port = htons(port); \ - } while(0) +#define IS_ADDR_LOOPBACK(addr) ((addr).s_addr == htonl(INADDR_LOOPBACK)) #endif /* Not IP v6 */ @@ -231,6 +229,8 @@ /* Maximum length of a node name == atom name */ #define MAXSYMLEN 255 +#define MAX_LISTEN_SOCKETS 16 + #define INBUF_SIZE 1024 #define OUTBUF_SIZE 1024 @@ -299,7 +299,8 @@ typedef struct { Connection *conn; Nodes nodes; fd_set orig_read_mask; - int listenfd; + int listenfd[MAX_LISTEN_SOCKETS]; + char *addresses; char **argv; } EpmdVars; diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c index ef471a473a..4d9b454f97 100644 --- a/erts/epmd/src/epmd_srv.c +++ b/erts/epmd/src/epmd_srv.c @@ -2,7 +2,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2010. All Rights Reserved. + * Copyright Ericsson AB 1998-2011. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -24,6 +24,10 @@ #include "epmd.h" /* Renamed from 'epmd_r4.h' */ #include "epmd_int.h" +#ifndef INADDR_NONE +# define INADDR_NONE 0xffffffff +#endif + /* * * This server is a local name server for Erlang nodes. Erlang nodes can @@ -79,89 +83,157 @@ static void print_names(EpmdVars*); void run(EpmdVars *g) { - int listensock; + struct EPMD_SOCKADDR_IN iserv_addr[MAX_LISTEN_SOCKETS]; + int listensock[MAX_LISTEN_SOCKETS]; + int num_sockets; int i; int opt; - struct EPMD_SOCKADDR_IN iserv_addr; + unsigned short sport = g->port; node_init(g); g->conn = conn_init(g); dbg_printf(g,2,"try to initiate listening port %d", g->port); - - if ((listensock = socket(FAMILY,SOCK_STREAM,0)) < 0) { - dbg_perror(g,"error opening stream socket"); - epmd_cleanup_exit(g,1); - } - g->listenfd = listensock; + + if (g->addresses != NULL) + { + char *tmp; + char *token; + int loopback_ok = 0; + + if ((tmp = (char *)malloc(strlen(g->addresses) + 1)) == NULL) + { + dbg_perror(g,"cannot allocate memory"); + epmd_cleanup_exit(g,1); + } + strcpy(tmp,g->addresses); + + for(token = strtok(tmp,", "), num_sockets = 0; + token != NULL; + token = strtok(NULL,", "), num_sockets++) + { + struct EPMD_IN_ADDR addr; +#ifdef HAVE_INET_PTON + int ret; + + if ((ret = inet_pton(FAMILY,token,&addr)) == -1) + { + dbg_perror(g,"cannot convert IP address to network format"); + epmd_cleanup_exit(g,1); + } + else if (ret == 0) +#elif !defined(EPMD6) + if ((addr.EPMD_S_ADDR = inet_addr(token)) == INADDR_NONE) +#endif + { + dbg_tty_printf(g,0,"cannot parse IP address \"%s\"",token); + epmd_cleanup_exit(g,1); + } + + if (IS_ADDR_LOOPBACK(addr)) + loopback_ok = 1; + + if (num_sockets - loopback_ok == MAX_LISTEN_SOCKETS - 1) + { + dbg_tty_printf(g,0,"cannot listen on more than %d IP addresses", + MAX_LISTEN_SOCKETS); + epmd_cleanup_exit(g,1); + } + + SET_ADDR(iserv_addr[num_sockets],addr.EPMD_S_ADDR,sport); + } + + free(tmp); + + if (!loopback_ok) + { + SET_ADDR(iserv_addr[num_sockets],EPMD_ADDR_LOOPBACK,sport); + num_sockets++; + } + } + else + { + SET_ADDR(iserv_addr[0],EPMD_ADDR_ANY,sport); + num_sockets = 1; + } + +#if !defined(__WIN32__) + /* We ignore the SIGPIPE signal that is raised when we call write + twice on a socket closed by the other end. */ + signal(SIGPIPE, SIG_IGN); +#endif /* * Initialize number of active file descriptors. * Stdin, stdout, and stderr are still open. - * One for the listen socket. */ - g->active_conn = 3+1; + g->active_conn = 3 + num_sockets; + g->max_conn -= num_sockets; + + FD_ZERO(&g->orig_read_mask); + + for (i = 0; i < num_sockets; i++) + { + if ((listensock[i] = socket(FAMILY,SOCK_STREAM,0)) < 0) + { + dbg_perror(g,"error opening stream socket"); + epmd_cleanup_exit(g,1); + } + g->listenfd[i] = listensock[i]; - /* - * Note that we must not enable the SO_REUSEADDR on Windows, - * because addresses will be reused even if they are still in use. - */ + /* + * Note that we must not enable the SO_REUSEADDR on Windows, + * because addresses will be reused even if they are still in use. + */ #if !defined(__WIN32__) - /* We ignore the SIGPIPE signal that is raised when we call write - twice on a socket closed by the other end. */ - signal(SIGPIPE, SIG_IGN); - - opt = 1; /* Set this option */ - if (setsockopt(listensock,SOL_SOCKET,SO_REUSEADDR,(char* ) &opt, - sizeof(opt)) <0) { - dbg_perror(g,"can't set sockopt"); - epmd_cleanup_exit(g,1); - } + opt = 1; + if (setsockopt(listensock[i],SOL_SOCKET,SO_REUSEADDR,(char* ) &opt, + sizeof(opt)) <0) + { + dbg_perror(g,"can't set sockopt"); + epmd_cleanup_exit(g,1); + } #endif - /* In rare cases select returns because there is someone - to accept but the request is withdrawn before the - accept function is called. We set the listen socket - to be non blocking to prevent us from being hanging - in accept() waiting for the next request. */ + /* In rare cases select returns because there is someone + to accept but the request is withdrawn before the + accept function is called. We set the listen socket + to be non blocking to prevent us from being hanging + in accept() waiting for the next request. */ #if (defined(__WIN32__) || defined(NO_FCNTL)) - opt = 1; - if (ioctl(listensock, FIONBIO, &opt) != 0) /* Gives warning in VxWorks */ + opt = 1; + /* Gives warning in VxWorks */ + if (ioctl(listensock[i], FIONBIO, &opt) != 0) #else - opt = fcntl(listensock, F_GETFL, 0); - if (fcntl(listensock, F_SETFL, opt | O_NONBLOCK) == -1) + opt = fcntl(listensock[i], F_GETFL, 0); + if (fcntl(listensock[i], F_SETFL, opt | O_NONBLOCK) == -1) #endif /* __WIN32__ || VXWORKS */ - dbg_perror(g,"failed to set non-blocking mode of listening socket %d", - listensock); + dbg_perror(g,"failed to set non-blocking mode of listening socket %d", + listensock[i]); - { /* store port number in unsigned short */ - unsigned short sport = g->port; - SET_ADDR_ANY(iserv_addr, FAMILY, sport); - } - - if(bind(listensock,(struct sockaddr*) &iserv_addr, sizeof(iserv_addr)) < 0 ) - { - if (errno == EADDRINUSE) + if (bind(listensock[i], (struct sockaddr*) &iserv_addr[i], + sizeof(iserv_addr[i])) < 0) { - dbg_tty_printf(g,1,"there is already a epmd running at port %d", - g->port); - epmd_cleanup_exit(g,0); - } - else - { - dbg_perror(g,"failed to bind socket"); - epmd_cleanup_exit(g,1); + if (errno == EADDRINUSE) + { + dbg_tty_printf(g,1,"there is already a epmd running at port %d", + g->port); + epmd_cleanup_exit(g,0); + } + else + { + dbg_perror(g,"failed to bind socket"); + epmd_cleanup_exit(g,1); + } } - } - - dbg_printf(g,2,"starting"); - - listen(listensock, SOMAXCONN); - - FD_ZERO(&g->orig_read_mask); - FD_SET(listensock,&g->orig_read_mask); + if(listen(listensock[i], SOMAXCONN) < 0) { + dbg_perror(g,"failed to listen on socket"); + epmd_cleanup_exit(g,1); + } + FD_SET(listensock[i],&g->orig_read_mask); + } dbg_tty_printf(g,2,"entering the main select() loop"); @@ -198,17 +270,18 @@ void run(EpmdVars *g) sleep(g->delay_accept); } - if (FD_ISSET(listensock,&read_mask)) { - if (do_accept(g, listensock) && g->active_conn < g->max_conn) { - /* - * The accept() succeeded, and we have at least one file - * descriptor still free, which means that another accept() - * could succeed. Go do do another select(), in case there - * are more incoming connections waiting to be accepted. - */ - goto select_again; + for (i = 0; i < num_sockets; i++) + if (FD_ISSET(listensock[i],&read_mask)) { + if (do_accept(g, listensock[i]) && g->active_conn < g->max_conn) { + /* + * The accept() succeeded, and we have at least one file + * descriptor still free, which means that another accept() + * could succeed. Go do do another select(), in case there + * are more incoming connections waiting to be accepted. + */ + goto select_again; + } } - } /* Check all open streams marked by select for data or a close. We also close all open sockets except ALIVE @@ -736,6 +809,7 @@ static int conn_open(EpmdVars *g,int fd) for (i = 0; i < g->max_conn; i++) { if (g->conn[i].open == EPMD_FALSE) { struct sockaddr_in si; + struct sockaddr_in di; #ifdef HAVE_SOCKLEN_T socklen_t st; #else @@ -756,12 +830,16 @@ static int conn_open(EpmdVars *g,int fd) /* 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 */ + /* Failure to get peername is regarded as non local host */ s->local_peer = EPMD_FALSE; } else { + /* Only 127.x.x.x and connections from the host's IP address + allowed, no false positives */ s->local_peer = - ((((unsigned) ntohl(si.sin_addr.s_addr)) & 0xFF000000U) == - 0x7F000000U); /* Only 127.x.x.x allowed, no false positives */ + (((((unsigned) ntohl(si.sin_addr.s_addr)) & 0xFF000000U) == + 0x7F000000U) || + (getsockname(s->fd,(struct sockaddr*) &di,&st) ? + EPMD_FALSE : si.sin_addr.s_addr == di.sin_addr.s_addr)); } dbg_tty_printf(g,2,(s->local_peer) ? "Local peer connected" : "Non-local peer connected"); diff --git a/erts/epmd/test/Makefile b/erts/epmd/test/Makefile index 13dad09ae3..54688fd90b 100644 --- a/erts/epmd/test/Makefile +++ b/erts/epmd/test/Makefile @@ -74,7 +74,7 @@ release_tests_spec: opt $(INSTALL_DIR) $(RELEPMDDIR) $(INSTALL_DATA) epmd.spec epmd.spec.vxworks $(ERL_FILES) \ $(EMAKEFILE) $(RELEPMDDIR) - chmod -f -R u+w $(RELEPMDDIR) + chmod -R u+w $(RELEPMDDIR) release_docs_spec: diff --git a/erts/epmd/test/epmd.spec b/erts/epmd/test/epmd.spec index 0e2496bc72..e72272cf94 100644 --- a/erts/epmd/test/epmd.spec +++ b/erts/epmd/test/epmd.spec @@ -1 +1 @@ -{topcase, {dir, "../epmd_test"}}. +{suites,"../epmd_test",all}. diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl index da69412e12..72c890503d 100644 --- a/erts/epmd/test/epmd_SUITE.erl +++ b/erts/epmd/test/epmd_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2010. All Rights Reserved. +%% Copyright Ericsson AB 1998-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -17,7 +17,7 @@ %% %CopyrightEnd% %% -module(epmd_SUITE). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -include_lib("kernel/include/file.hrl"). @@ -35,7 +35,9 @@ -record(node_info, {port, node_type, prot, lvsn, hvsn, node_name, extra}). % Test server specific exports --export([all/1, init_per_testcase/2, fin_per_testcase/2]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, + init_per_testcase/2, end_per_testcase/2]). -export( [ @@ -64,7 +66,7 @@ returns_valid_empty_extra/1, returns_valid_populated_extra_with_nulls/1, - buffer_overrun/1, + buffer_overrun_1/1, buffer_overrun_2/1, no_nonlocal_register/1, @@ -101,42 +103,37 @@ %% all/1 %% -all(suite) -> - [ - register_name, - register_names_1, - register_names_2, - register_duplicate_name, - get_port_nr, - slow_get_port_nr, - unregister_others_name_1, - unregister_others_name_2, - register_overflow, - name_with_null_inside, - name_null_terminated, - stupid_names_req, - - no_data, - one_byte, - two_bytes, - partial_packet, - zero_length, - too_large, - alive_req_too_small_1, - alive_req_too_small_2, - alive_req_too_large, - - returns_valid_empty_extra, +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [register_name, register_names_1, register_names_2, + register_duplicate_name, get_port_nr, slow_get_port_nr, + unregister_others_name_1, unregister_others_name_2, + register_overflow, name_with_null_inside, + name_null_terminated, stupid_names_req, no_data, + one_byte, two_bytes, partial_packet, zero_length, + too_large, alive_req_too_small_1, alive_req_too_small_2, + alive_req_too_large, returns_valid_empty_extra, returns_valid_populated_extra_with_nulls, + {group, buffer_overrun}, no_nonlocal_register, + no_nonlocal_kill, no_live_killing]. + +groups() -> + [{buffer_overrun, [], + [buffer_overrun_1, buffer_overrun_2]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. - buffer_overrun, - %buffer_overrun_1, - %buffer_overrun_2, +end_per_group(_GroupName, Config) -> + Config. - no_nonlocal_register, - no_nonlocal_kill, - no_live_killing - ]. %% %% Run before and after each test case @@ -147,7 +144,7 @@ init_per_testcase(_Func, Config) -> cleanup(), [{watchdog, Dog} | Config]. -fin_per_testcase(_Func, Config) -> +end_per_testcase(_Func, Config) -> cleanup(), Dog = ?config(watchdog, Config), catch test_server:timetrap_cancel(Dog), % We may have canceled already @@ -725,8 +722,6 @@ returns_valid_populated_extra_with_nulls(Config) when is_list(Config) -> ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -buffer_overrun(suite) -> - [buffer_overrun_1,buffer_overrun_2]. buffer_overrun_1(suite) -> []; |