From 6499bf2816c3834059ebdeff75d98f310cb305b4 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Tue, 24 Aug 2010 12:09:17 +0200 Subject: Remove very old protocol from EPMD --- erts/epmd/src/epmd.h | 21 ++++---- erts/epmd/src/epmd_srv.c | 132 ----------------------------------------------- 2 files changed, 9 insertions(+), 144 deletions(-) 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_srv.c b/erts/epmd/src/epmd_srv.c index c836bf0bb7..12ebd7f415 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); @@ -424,100 +391,6 @@ static void do_request(g, fd, s, buf, bsize) 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"); @@ -990,11 +863,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, -- cgit v1.2.3 From f5be3aeaef131d19741084dbf8fee16458d31513 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Tue, 24 Aug 2010 14:43:18 +0200 Subject: Remove all support for ancient EPMD protocol --- erts/epmd/test/epmd_SUITE.erl | 109 +++++++------- lib/erl_interface/src/epmd/ei_epmd.h | 11 +- lib/erl_interface/src/epmd/epmd_port.c | 71 +-------- lib/erl_interface/src/epmd/epmd_publish.c | 68 +-------- .../java_src/com/ericsson/otp/erlang/OtpEpmd.java | 161 +-------------------- lib/kernel/src/erl_epmd.erl | 86 +---------- lib/kernel/src/erl_epmd.hrl | 17 +-- 7 files changed, 75 insertions(+), 448 deletions(-) diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl index 91e09faf75..88980fec63 100644 --- a/erts/epmd/test/epmd_SUITE.erl +++ b/erts/epmd/test/epmd_SUITE.erl @@ -82,11 +82,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). @@ -148,7 +145,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 +155,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 +167,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 +179,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 +189,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 +238,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 +250,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 +261,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 +272,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 +376,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 +395,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,7 +415,7 @@ unregister_others_name_1(doc) -> ["Unregister name of other node"]; unregister_others_name_1(suite) -> []; -unregister_others_name_1(Config) when list(Config) -> +unregister_others_name_1(Config) when is_list(Config) -> ?line ok = epmdrun(), ?line {ok,RSock} = register_node("foo"), ?line {ok,Sock} = connect(), @@ -441,7 +432,7 @@ unregister_others_name_2(doc) -> ["Unregister name of other node"]; unregister_others_name_2(suite) -> []; -unregister_others_name_2(Config) when list(Config) -> +unregister_others_name_2(Config) when is_list(Config) -> ?line ok = epmdrun(), ?line {ok,Sock} = connect(), M = [?EPMD_STOP_REQ,"xxx42"], @@ -456,7 +447,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 +537,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 +550,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 +564,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 +578,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 +592,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,7 +606,7 @@ 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, @@ -631,10 +622,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 +638,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 +654,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 +671,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 +685,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 +698,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"), @@ -858,9 +853,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); diff --git a/lib/erl_interface/src/epmd/ei_epmd.h b/lib/erl_interface/src/epmd/ei_epmd.h index 40e5ece572..ccacfed244 100644 --- a/lib/erl_interface/src/epmd/ei_epmd.h +++ b/lib/erl_interface/src/epmd/ei_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 @@ -40,20 +40,13 @@ #define EI_MYPROTO 0 /* tcp/ip */ #endif -/* epmd r3 protocol */ -#ifndef EI_EPMD_ALIVE_REQ -#define EI_EPMD_ALIVE_REQ 'a' -#define EI_EPMD_ALIVE_OK_RESP 'Y' -#define EI_EPMD_PORT_REQ 'p' -#define EI_EPMD_STOP_REQ 's' -#endif - /* epmd r4 */ #ifndef EI_EPMD_ALIVE2_REQ #define EI_EPMD_ALIVE2_REQ 120 #define EI_EPMD_ALIVE2_RESP 121 #define EI_EPMD_PORT2_REQ 122 #define EI_EPMD_PORT2_RESP 119 +#define EI_EPMD_STOP_REQ 's' #endif /* internal functions */ diff --git a/lib/erl_interface/src/epmd/epmd_port.c b/lib/erl_interface/src/epmd/epmd_port.c index 663b38d2d4..3dd3da7aae 100644 --- a/lib/erl_interface/src/epmd/epmd_port.c +++ b/lib/erl_interface/src/epmd/epmd_port.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 @@ -92,64 +92,6 @@ int ei_epmd_connect_tmo(struct in_addr *inaddr, unsigned ms) return sd; } -/* get the given node's listen port using old epmd protocol */ -static int ei_epmd_r3_port (struct in_addr *addr, const char *alive, - unsigned ms) -{ - char buf[EPMDBUF]; - char *s = buf; - int len = strlen(alive) + 1; - int fd; - int port; - int res; -#if defined(VXWORKS) - char ntoabuf[32]; -#endif - - put16be(s,len); - put8(s,EI_EPMD_PORT_REQ); - strcpy(s,alive); - - /* connect to epmd */ - if ((fd = ei_epmd_connect_tmo(addr,ms)) < 0) - { - /* ei_epmd_connect_tmo() sets erl_errno */ - return -1; - } - - if ((res = ei_write_fill_t(fd, buf, len+2, ms)) != len+2) { - closesocket(fd); - erl_errno = (res == -2) ? ETIMEDOUT : EIO; - return -1; - } - -#ifdef VXWORKS - /* FIXME use union/macro for level. Correct level? */ - if (ei_tracelevel > 2) { - inet_ntoa_b(*addr,ntoabuf); - EI_TRACE_CONN2("ei_epmd_r3_port", - "-> PORT_REQ alive=%s ip=%s",alive,ntoabuf); - } -#else - EI_TRACE_CONN2("ei_epmd_r3_port", - "-> PORT_REQ alive=%s ip=%s",alive,inet_ntoa(*addr)); -#endif - - if ((res = ei_read_fill_t(fd, buf, 2, ms)) != 2) { - EI_TRACE_ERR0("ei_epmd_r3_port","<- CLOSE"); - closesocket(fd); - erl_errno = (res == -2) ? ETIMEDOUT : EIO; - return -1; - } - closesocket(fd); - s = buf; - port = get16be(s); - - EI_TRACE_CONN1("ei_epmd_r3_port","<- PORT_RESP port=%d",port); - - return port; -} - static int ei_epmd_r4_port (struct in_addr *addr, const char *alive, int *dist, unsigned ms) { @@ -285,15 +227,6 @@ int ei_epmd_port_tmo (struct in_addr *addr, const char *alive, int *dist, { int i; - /* try the new one first, then the old one */ - i = ei_epmd_r4_port(addr,alive,dist,ms); - - /* -2: new protocol not understood */ - if (i == -2) { - *dist = 0; - i = ei_epmd_r3_port(addr,alive,ms); - } - - return i; + return ei_epmd_r4_port(addr,alive,dist,ms); } diff --git a/lib/erl_interface/src/epmd/epmd_publish.c b/lib/erl_interface/src/epmd/epmd_publish.c index 09b3dce43b..a9b8727747 100644 --- a/lib/erl_interface/src/epmd/epmd_publish.c +++ b/lib/erl_interface/src/epmd/epmd_publish.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 @@ -55,62 +55,6 @@ /* publish our listen port and alive name */ /* return the (useless) creation number */ -static int ei_epmd_r3_publish (int port, const char *alive, unsigned ms) -{ - char buf[EPMDBUF]; - char *s = buf; - int fd; - int len = strlen(alive) + 3; - int res,creation; - - s = buf; - put16be(s,len); - put8(s,EI_EPMD_ALIVE_REQ); - put16be(s,port); - strcpy(s, alive); - - if ((fd = ei_epmd_connect_tmo(NULL,ms)) < 0) return fd; - - if ((res = ei_write_fill_t(fd, buf, len+2, ms)) != len+2) { - closesocket(fd); - erl_errno = (res == -2) ? ETIMEDOUT : EIO; - return -1; - } - - EI_TRACE_CONN2("ei_epmd_r3_publish", - "-> ALIVE_REQ alive=%s port=%d",alive,port); - - if ((res = ei_read_fill_t(fd, buf, 3, ms)) != 3) { - closesocket(fd); - erl_errno = (res == -2) ? ETIMEDOUT : EIO; - return -1; - } - - s = buf; - if ((res=get8(s)) != EI_EPMD_ALIVE_OK_RESP) { - EI_TRACE_ERR1("ei_epmd_r3_publish", - "<- ALIVE_NOK result=%d (failure)",res); - closesocket(fd); - erl_errno = EIO; - return -1; - } - - creation = get16be(s); - - EI_TRACE_CONN1("ei_epmd_r3_publish","<- ALIVE_OK creation=%d",creation); - - /* Don't close fd here! It keeps us registered with epmd */ - - /* probably should save fd so we can close it later... */ - /* epmd_saveconn(OPEN,fd,alive); */ - - /* return the creation number, for no good reason */ - /* return creation; */ - - /* no! return the descriptor */ - return fd; -} - /* publish our listen port and alive name */ /* return the (useless) creation number */ /* this protocol is a lot more complex than the old one */ @@ -200,15 +144,7 @@ int ei_epmd_publish(int port, const char *alive) int ei_epmd_publish_tmo(int port, const char *alive, unsigned ms) { - int i; - - /* try the new one first, then the old one */ - i = ei_epmd_r4_publish(port,alive, ms); - - /* -2: new protocol not understood */ - if (i == -2) i = ei_epmd_r3_publish(port,alive, ms); - - return i; + return ei_epmd_r4_publish(port,alive, ms);; } diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java index 3bb678c2cc..deac528133 100644 --- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2000-2009. All Rights Reserved. + * Copyright Ericsson AB 2000-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 @@ -71,11 +71,6 @@ public class OtpEpmd { // common values private static final byte stopReq = (byte) 115; - // version specific value - private static final byte port3req = (byte) 112; - private static final byte publish3req = (byte) 97; - private static final byte publish3ok = (byte) 89; - private static final byte port4req = (byte) 122; private static final byte port4resp = (byte) 119; private static final byte publish4req = (byte) 120; @@ -123,11 +118,7 @@ public class OtpEpmd { * if there was no response from the name server. */ public static int lookupPort(final AbstractNode node) throws IOException { - try { return r4_lookupPort(node); - } catch (final IOException e) { - return r3_lookupPort(node); - } } /** @@ -147,11 +138,7 @@ public class OtpEpmd { throws IOException { Socket s = null; - try { - s = r4_publish(node); - } catch (final IOException e) { - s = r3_publish(node); - } + s = r4_publish(node); node.setEpmd(s); @@ -196,67 +183,6 @@ public class OtpEpmd { } } - private static int r3_lookupPort(final AbstractNode node) - throws IOException { - int port = 0; - Socket s = null; - - try { - final OtpOutputStream obuf = new OtpOutputStream(); - s = new Socket(node.host(), EpmdPort.get()); - - // build and send epmd request - // length[2], tag[1], alivename[n] (length = n+1) - obuf.write2BE(node.alive().length() + 1); - obuf.write1(port3req); - obuf.writeN(node.alive().getBytes()); - - // send request - obuf.writeTo(s.getOutputStream()); - - if (traceLevel >= traceThreshold) { - System.out.println("-> LOOKUP (r3) " + node); - } - - // receive and decode reply - final byte[] tmpbuf = new byte[100]; - - s.getInputStream().read(tmpbuf); - final OtpInputStream ibuf = new OtpInputStream(tmpbuf, 0); - - port = ibuf.read2BE(); - } catch (final IOException e) { - if (traceLevel >= traceThreshold) { - System.out.println("<- (no response)"); - } - throw new IOException("Nameserver not responding on " + node.host() - + " when looking up " + node.alive()); - } catch (final OtpErlangDecodeException e) { - if (traceLevel >= traceThreshold) { - System.out.println("<- (invalid response)"); - } - throw new IOException("Nameserver not responding on " + node.host() - + " when looking up " + node.alive()); - } finally { - try { - if (s != null) { - s.close(); - } - } catch (final IOException e) { /* ignore close errors */ - } - s = null; - } - - if (traceLevel >= traceThreshold) { - if (port == 0) { - System.out.println("<- NOT FOUND"); - } else { - System.out.println("<- PORT " + port); - } - } - return port; - } - private static int r4_lookupPort(final AbstractNode node) throws IOException { int port = 0; @@ -288,8 +214,6 @@ public class OtpEpmd { final int n = s.getInputStream().read(tmpbuf); if (n < 0) { - // this was an r3 node => not a failure (yet) - s.close(); throw new IOException("Nameserver not responding on " + node.host() + " when looking up " + node.alive()); @@ -342,81 +266,13 @@ public class OtpEpmd { return port; } - private static Socket r3_publish(final OtpLocalNode node) - throws IOException { - Socket s = null; - - try { - final OtpOutputStream obuf = new OtpOutputStream(); - s = new Socket((String) null, EpmdPort.get()); - - obuf.write2BE(node.alive().length() + 3); - - obuf.write1(publish3req); - obuf.write2BE(node.port()); - obuf.writeN(node.alive().getBytes()); - - // send request - obuf.writeTo(s.getOutputStream()); - if (traceLevel >= traceThreshold) { - System.out.println("-> PUBLISH (r3) " + node + " port=" - + node.port()); - } - - final byte[] tmpbuf = new byte[100]; - - final int n = s.getInputStream().read(tmpbuf); - - if (n < 0) { - s.close(); - if (traceLevel >= traceThreshold) { - System.out.println("<- (no response)"); - } - return null; - } - - final OtpInputStream ibuf = new OtpInputStream(tmpbuf, 0); - - if (ibuf.read1() == publish3ok) { - node.creation = ibuf.read2BE(); - if (traceLevel >= traceThreshold) { - System.out.println("<- OK"); - } - return s; // success - don't close socket - } - } catch (final IOException e) { - // epmd closed the connection = fail - if (s != null) { - s.close(); - } - if (traceLevel >= traceThreshold) { - System.out.println("<- (no response)"); - } - throw new IOException("Nameserver not responding on " + node.host() - + " when publishing " + node.alive()); - } catch (final OtpErlangDecodeException e) { - if (s != null) { - s.close(); - } - if (traceLevel >= traceThreshold) { - System.out.println("<- (invalid response)"); - } - throw new IOException("Nameserver not responding on " + node.host() - + " when publishing " + node.alive()); - } - - if (s != null) { - s.close(); - } - return null; // failure - } - /* - * this function will get an exception if it tries to talk to an r3 epmd, or - * if something else happens that it cannot forsee. In both cases we return - * an exception (and the caller should try again, using the r3 protocol). If - * we manage to successfully communicate with an r4 epmd, we return either - * the socket, or null, depending on the result. + * this function will get an exception if it tries to talk to a + * very old epmd, or if something else happens that it cannot + * forsee. In both cases we return an exception. We no longer + * support r3, so the exception is fatal. If we manage to + * successfully communicate with an r4 epmd, we return either the + * socket, or null, depending on the result. */ private static Socket r4_publish(final OtpLocalNode node) throws IOException { @@ -454,7 +310,6 @@ public class OtpEpmd { final int n = s.getInputStream().read(tmpbuf); if (n < 0) { - // this was an r3 node => not a failure (yet) if (s != null) { s.close(); } diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl index 4a22637304..91af49f303 100644 --- a/lib/kernel/src/erl_epmd.erl +++ b/lib/kernel/src/erl_epmd.erl @@ -210,19 +210,6 @@ open({A,B,C,D,E,F,G,H}=EpmdAddr, Timeout) when ?ip6(A,B,C,D,E,F,G,H) -> close(Socket) -> gen_tcp:close(Socket). - -do_register_node_v0(NodeName, TcpPort) -> - case open() of - {ok, Socket} -> - Name = cstring(NodeName), - Len = 1+2+length(Name), - gen_tcp:send(Socket, [?int16(Len), ?EPMD_ALIVE, - ?int16(TcpPort), Name]), - wait_for_reg_reply_v0(Socket, []); - Error -> - Error - end. - do_register_node(NodeName, TcpPort) -> case open() of {ok, Socket} -> @@ -240,14 +227,7 @@ do_register_node(NodeName, TcpPort) -> Name, ?int16(Elen), Extra]), - case wait_for_reg_reply(Socket, []) of - {error, epmd_close} -> - %% could be old epmd; try old protocol -% erlang:display('trying old'), - do_register_node_v0(NodeName, TcpPort); - Other -> - Other - end; + wait_for_reg_reply(Socket, []); Error -> Error end. @@ -305,41 +285,9 @@ wait_for_reg_reply(Socket, SoFar) -> {error, no_reg_reply_from_epmd} end. -wait_for_reg_reply_v0(Socket, SoFar) -> - receive - {tcp, Socket, Data0} -> - case SoFar ++ Data0 of - [$Y, A, B] -> - {alive, Socket, ?u16(A, B)}; - Data when length(Data) < 3 -> - wait_for_reg_reply(Socket, Data); - Garbage -> - {error, {garbage_from_epmd, Garbage}} - end; - {tcp_closed, Socket} -> - {error, duplicate_name} % A guess -- the most likely reason. - after 10000 -> - gen_tcp:close(Socket), - {error, no_reg_reply_from_epmd} - end. %% %% Lookup a node "Name" at Host %% -get_port_v0(Node, EpmdAddress) -> - case open(EpmdAddress) of - {ok, Socket} -> - Name = cstring(Node), - Len = 1+length(Name), - gen_tcp:send(Socket, [?int16(Len),?EPMD_PORT_PLEASE, Name]), - wait_for_port_reply_v0(Socket, []); - _Error -> - ?port_please_failure(), - noport - end. - -%%% Not used anymore -%%% get_port(Node, EpmdAddress) -> -%%% get_port(Node, EpmdAddress, infinity). get_port(Node, EpmdAddress, Timeout) -> case open(EpmdAddress, Timeout) of @@ -347,40 +295,12 @@ get_port(Node, EpmdAddress, Timeout) -> Name = to_string(Node), Len = 1+length(Name), gen_tcp:send(Socket, [?int16(Len),?EPMD_PORT_PLEASE2_REQ, Name]), - Reply = wait_for_port_reply(Socket, []), - case Reply of - closed -> - get_port_v0(Node, EpmdAddress); - Other -> - Other - end; + wait_for_port_reply(Socket, []); _Error -> ?port_please_failure2(_Error), noport end. -wait_for_port_reply_v0(Socket, SoFar) -> - receive - {tcp, Socket, Data0} -> -% io:format("got ~p~n", [Data0]), - case SoFar ++ Data0 of - [A, B] -> - wait_for_close(Socket, {port, ?u16(A, B), 0}); -% wait_for_close(Socket, {port, ?u16(A, B)}); - Data when length(Data) < 2 -> - wait_for_port_reply_v0(Socket, Data); - Garbage -> - ?port_please_failure(), - {error, {garbage_from_epmd, Garbage}} - end; - {tcp_closed, Socket} -> - ?port_please_failure(), - noport - after 10000 -> - ?port_please_failure(), - gen_tcp:close(Socket), - noport - end. wait_for_port_reply(Socket, SoFar) -> receive @@ -487,8 +407,6 @@ wait_for_close(Socket, Reply) -> %% %% Creates a (flat) null terminated string from atom or list. %% -cstring(S) when is_atom(S) -> cstring(atom_to_list(S)); -cstring(S) when is_list(S) -> S ++ [0]. to_string(S) when is_atom(S) -> atom_to_list(S); to_string(S) when is_list(S) -> S. diff --git a/lib/kernel/src/erl_epmd.hrl b/lib/kernel/src/erl_epmd.hrl index 47ab6195d8..5a50fda508 100644 --- a/lib/kernel/src/erl_epmd.hrl +++ b/lib/kernel/src/erl_epmd.hrl @@ -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,16 +17,13 @@ %% %CopyrightEnd% %% --define(EPMD_ALIVE, $a). --define(EPMD_PORT_PLEASE, $p). --define(EPMD_NAMES, $n). --define(EPMD_DUMP, $d). --define(EPMD_KILL, $k). --define(EPMD_STOP, $s). - --define(EPMD_ALIVE_OK, $Y). - -define(EPMD_ALIVE2_REQ, $x). -define(EPMD_PORT_PLEASE2_REQ, $z). -define(EPMD_ALIVE2_RESP, $y). -define(EPMD_PORT2_RESP, $w). +-define(EPMD_NAMES, $n). + +%% Commands used only by interactive client +-define(EPMD_DUMP, $d). +-define(EPMD_KILL, $k). +-define(EPMD_STOP, $s). -- cgit v1.2.3 From 716d3f57b471b2e2c3b5772008f5d32767c6cbeb Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Wed, 25 Aug 2010 11:47:11 +0200 Subject: Remove two buffer overflow vulnerabilities in EPMD --- erts/epmd/src/epmd_int.h | 1 + erts/epmd/src/epmd_srv.c | 28 ++++++++++++------- erts/epmd/test/epmd_SUITE.erl | 62 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 79 insertions(+), 12 deletions(-) diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h index 5ead553f36..d69d8c93ef 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% * diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c index 12ebd7f415..9e470e41b0 100644 --- a/erts/epmd/src/epmd_srv.c +++ b/erts/epmd/src/epmd_srv.c @@ -270,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; } @@ -372,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; @@ -382,13 +382,6 @@ 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'; - switch (*buf) { case EPMD_ALIVE2_REQ: @@ -398,7 +391,7 @@ static void do_request(g, fd, s, buf, bsize) in network byte order, and yyyyyy is symname, possibly null terminated. */ - if (bsize <= 13) + if (bsize <= 14) /* at least one character for the node name */ { dbg_printf(g,0,"packet to small for request ALIVE2_REQ (%d)",bsize); return; @@ -421,7 +414,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"); @@ -888,6 +891,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 88980fec63..5bcbe96b31 100644 --- a/erts/epmd/test/epmd_SUITE.erl +++ b/erts/epmd/test/epmd_SUITE.erl @@ -63,7 +63,9 @@ 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/1, + buffer_overrun_2/1 ]). @@ -121,7 +123,10 @@ all(suite) -> alive_req_too_large, returns_valid_empty_extra, - returns_valid_populated_extra_with_nulls + returns_valid_populated_extra_with_nulls, + + buffer_overrun_1, + buffer_overrun_2 ]. %% @@ -705,6 +710,59 @@ returns_valid_populated_extra_with_nulls(Config) when is_list(Config) -> ?line ok = close(Sock), ok. + +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(), + gen_tcp:send(E,[<>,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(), + gen_tcp:send(E,[<>,Bin]), + Z = recv(E,2), + gen_tcp:close(E), + (Z =:= closed) or (Z =:= {ok, [$y,1]}) + catch + _:_ -> + false + end. + +alltrue([]) -> + true; +alltrue([true|T]) -> + alltrue(T); +alltrue([_|_]) -> + false. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Terminate all tests with killing epmd. -- cgit v1.2.3 From 1ff4783ab8b1bdb32ced6072eb193896b429d115 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Wed, 25 Aug 2010 18:04:56 +0200 Subject: Fix anomalies in epmd not yet reported as security issues Use erts_(v)snprintf to ensure no buffer overruns in debug printouts. Disallow everything except port and name requests from remote nodes. Disallow kill command even from localhost if alive nodes exist. -relaxed_command_check when starting epmd returns the possibility to kill this epmd when nodes are alive (from localhost). Disallow stop command completely except if -relaxed_command_check is given when epmd was started. Environment variable ERL_EPMD_RELAXED_COMMAND_CHECK can be set to always get -relaxed_command_check. --- erts/epmd/src/Makefile.in | 25 +++++-- erts/epmd/src/epmd.c | 48 ++++++++++-- erts/epmd/src/epmd_int.h | 8 +- erts/epmd/src/epmd_srv.c | 51 +++++++++++++ erts/epmd/test/epmd_SUITE.erl | 166 ++++++++++++++++++++++++++++++++++++------ 5 files changed, 262 insertions(+), 36 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..f195fdac8c 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 @@ -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); @@ -387,10 +393,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, "usage: epmd [-d|-debug] [DbgExtra...] [-port No] [-daemon] [-allow_brutal_kill]\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, "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); @@ -401,6 +407,14 @@ static void usage(EpmdVars *g) 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, " -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_timout 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"); @@ -413,6 +427,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, " -allow_brutal_kill 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 -allow_brutal_kill was given when \n"); + fprintf(stderr, " epmd was started).\n"); epmd_cleanup_exit(g,1); } @@ -432,20 +458,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 +482,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 +572,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_int.h b/erts/epmd/src/epmd_int.h index d69d8c93ef..c2558d52a1 100644 --- a/erts/epmd/src/epmd_int.h +++ b/erts/epmd/src/epmd_int.h @@ -246,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 */ @@ -287,6 +289,7 @@ typedef struct { int debug; int silent; int is_daemon; + int brutal_kill; unsigned packet_timeout; unsigned delay_accept; unsigned delay_write; @@ -308,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 9e470e41b0..3ba8a93fbd 100644 --- a/erts/epmd/src/epmd_srv.c +++ b/erts/epmd/src/epmd_srv.c @@ -386,6 +386,10 @@ static void do_request(g, fd, s, buf, bsize) { 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 @@ -555,6 +559,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; @@ -604,7 +612,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"); @@ -614,6 +634,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); @@ -701,6 +730,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]; @@ -710,6 +747,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 */ diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl index 5bcbe96b31..51da934ac4 100644 --- a/erts/epmd/test/epmd_SUITE.erl +++ b/erts/epmd/test/epmd_SUITE.erl @@ -65,7 +65,10 @@ returns_valid_empty_extra/1, returns_valid_populated_extra_with_nulls/1, buffer_overrun_1/1, - buffer_overrun_2/1 + buffer_overrun_2/1, + no_nonlocal_register/1, + no_nonlocal_kill/1, + no_live_killing/1 ]). @@ -125,8 +128,12 @@ all(suite) -> returns_valid_empty_extra, returns_valid_populated_extra_with_nulls, - buffer_overrun_1, - buffer_overrun_2 + buffer_overrun_1, + buffer_overrun_2, + + no_nonlocal_register, + no_nonlocal_kill, + no_live_killing ]. %% @@ -421,7 +428,7 @@ unregister_others_name_1(doc) -> unregister_others_name_1(suite) -> []; unregister_others_name_1(Config) when is_list(Config) -> - ?line ok = epmdrun(), + ?line ok = epmdrun("-relaxed_command_check"), ?line {ok,RSock} = register_node("foo"), ?line {ok,Sock} = connect(), M = [?EPMD_STOP_REQ,"foo"], @@ -438,7 +445,7 @@ unregister_others_name_2(doc) -> unregister_others_name_2(suite) -> []; unregister_others_name_2(Config) when is_list(Config) -> - ?line ok = epmdrun(), + ?line ok = epmdrun("-relaxed_command_check"), ?line {ok,Sock} = connect(), M = [?EPMD_STOP_REQ,"xxx42"], ?line ok = send(Sock,[size16(M),M]), @@ -710,6 +717,7 @@ returns_valid_populated_extra_with_nulls(Config) when is_list(Config) -> ?line ok = close(Sock), ok. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% buffer_overrun_1(suite) -> []; @@ -762,6 +770,111 @@ 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,[<>,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. @@ -784,16 +897,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)). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -810,16 +931,19 @@ osrun(Cmd) -> % 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). % Try a few times before giving up -connect(Port, Mode) -> - case connect_repeat(?CONN_RETRY, Port, Mode) of +connect(Addr, Port, Mode) -> + case connect_repeat(Addr, ?CONN_RETRY, Port, Mode) of {ok,Sock} -> {ok,Sock}; {error,timeout} -> @@ -836,25 +960,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) -> + connect_mode(Addr,Port, Mode); +connect_repeat(Addr,Retry, Port, Mode) -> + 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); + connect_repeat(Addr, Retry - 1, Port, Mode); 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). -- cgit v1.2.3 From b35b4eeb6f0cb25c3c5b5e785939fb829f77ef6a Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Fri, 27 Aug 2010 11:08:32 +0200 Subject: Document epmd and it's options properly and fixup help text --- erts/doc/src/epmd.xml | 252 ++++++++++++++++++++++++++++++++++++++++++-------- erts/epmd/src/epmd.c | 13 +-- 2 files changed, 218 insertions(+), 47 deletions(-) diff --git a/erts/doc/src/epmd.xml b/erts/doc/src/epmd.xml index 796ab3820b..f01cf90a36 100644 --- a/erts/doc/src/epmd.xml +++ b/erts/doc/src/epmd.xml @@ -4,7 +4,7 @@
- 19962009 + 19962010 Ericsson AB. All Rights Reserved. @@ -31,9 +31,23 @@ A epmd.xml
+ epmd - Erlang Port Mapper Daemon + +

Erlang Port Mapper Daemon

+ + + +

Starts the port mapper daemon

+
+ + +

Communicates with a running port mapper daemon

+
+
+
+

This daemon acts as a name server on all hosts involved in distributed Erlang computations. When an Erlang node starts, the node has a name and it obtains an address from the host @@ -46,47 +60,170 @@ The job of the daemon is to keep track of which node name listens on which address. Hence, map symbolic node names to machine addresses.

-

The daemon is started automatically by the Erlang start-up script.

-

The program can also be used for a variety of other - purposes, for example checking the DNS (Domain Name System) - configuration of a host.

+ +

The TCP/IP epmd daemon actually only keeps track of + the Name (first) part of an Erlang node name, the Host + part (whatever is after the is implicit in the + node name where the epmd daemon was actually contacted, + as is the IP address where the Erlang node can be + reached. Consistent and correct TCP naming services are + therefore required for an Erlang network to function + correctly.

+ + + Starting the port mapper daemon + + +

The daemon is started automatically by the erl + command if the node is to be distributed and there is no + running instance present. If automatically launched, + environment variables has to be used to alter the behavior of + the daemon. See the Environment + variables section below.

+ +

If the -daemon argument is not given, the + runs as a normal program with the + controlling terminal of the shell in which it is + started. Normally, it should run as a daemon.

+ +

Regular start-up options are described in the + Regular options + section below.

+ +

The DbgExtra options are described in the + DbgExtra options + section below.

+ +
+ Communicating with a running port mapper daemon + + +

Communicating with the running epmd daemon by means of the + epmd program is done primarily for debugging + purposes.

+ +

The different queries are described in the Interactive options + section below.

+ +
+
- - - epmd [-daemon] - Start a name server as a daemon - -

Starts a name server as a daemon. If it has no argument, the - runs as a normal program with the controlling terminal - of the shell in which it is started. Normally, it should run as a - daemon.

-
-
- - epmd -names - Request the names of the registered Erlang nodes on this host - -

Requests the names of the local Erlang nodes has - registered.

-
-
- - epmd -kill - Kill the process - -

Kills the process.

-
-
- - epmd -help - List options - -

Write short info about the usage including some debugging - options not listed here.

-
-
-
+
+ + Regular options + +

These options are available when starting the actual name server. The name server is normally started automatically by the erl command (if not already available), but it can also be started at i.e. system start-up.

+ + + +

Let this instance of epmd listen to another TCP port than + default 4369. This can be also be set using the + environment variable, see the + section Environment + variables below

+
+ + + +

Enable debug output. The more -d flags given, the more + debug output you will get (to a certain limit). This option is + most useful when the epmd daemon is not started as a daemon.

+
+ + +

Start epmd detached from the controlling terminal. Logging will end up in syslog when available and correctly configured. If the epmd daemon is started at boot, this option should definitely be used. It is also used when the erl command automatically starts epmd.

+
+ + +

Start the epmd program with relaxed command checking (mostly for backward compatibility). This affects the following:

+ + +

With relaxed command checking, the epmd daemon can be killed from the localhost with i.e. epmd -kill even if there are active nodes registered. Normally only daemons with an empty node database can be killed with the epmd -kill command.

+
+ +

The epmd -stop command (and the corresponding messages to epmd, as can be given using erl_interface/ei) is normally always ignored, as it opens up for strange situation when two nodes of the same name can be alive at the same time. A node unregisters itself by just closing the connection to epmd, why the stop command was only intended for use in debugging situations.

+

With relaxed command checking enabled, you can forcibly unregister live nodes.

+
+
+

Relaxed command checking can also be enabled by setting the environment variable ERL_EPMD_RELAXED_COMMAND_CHECK prior to starting epmd.

+

Only use relaxed command checking on systems with very limited interactive usage.

+
+
+
+
+ + DbgExtra options +

These options are purely for debugging and testing epmd clients, they should not be used in normal operation.

+ + + + +

Set the number of seconds a connection can be + inactive before epmd times out and closes the + connection (default 60).

+
+ + +

To simulate a busy server you can insert a delay between epmd + gets notified about that a new connection is requested and + when the connections gets accepted.

+
+ + +

Also a simulation of a busy server. Inserts + a delay before a reply is sent.

+
+
+
+
+ + Interactive options +

These options make epmd run as an interactive command displaying the results of sending queries ta an already running instance of epmd. The epmd contacted is always on the local node, but the -port option can be used to select between instances if several are running using different port on the host.

+ + + +

Contacts the epmd listening on the given TCP port number + (default 4369). This can be also be set using the + environment variable, see the + section Environment + variables below

+
+ + +

List names registered with the currently running epmd

+
+ + +

Kill the currently running epmd.

+ +

Killing the running epmd is only allowed if epmd + -names show an empty database or + -relaxed_command_check was given when the running + instance of epmd was started. Note that + -relaxed_command_check is given when starting the + daemon that is to accept killing when it has live nodes + registered. When running epmd interactively, + -relaxed_command_check has no effect. A daemon that is + started without relaxed command checking has to be killed + using i.e. signals or some other OS specific method if it has + active clients registered.

+
+ + +

Forcibly unregister a live node from epmd's database

+ +

This command can only be used when contacting epmd + instances started with the -relaxed_command_check + flag. Note that relaxed command checking has to be enabled for + the epmd daemon contacted, When running epmd + interactively, + -relaxed_command_check has no effect.

+
+
+
Environment variables @@ -99,6 +236,15 @@ independent clusters of nodes, to co-exist on the same host. All nodes in a cluster must use the same epmd port number.

+ + +

If set prior to start, the epmd daemon will behave + as if the -relaxed_command_check option was given at + start-up. If consequently setting this option before starting + the Erlang virtual machine, the automatically started + epmd will accept the -kill and -stop + commands without restrictions.

+
@@ -116,5 +262,29 @@ silently be ignored.

+
+ Access restrictions +

The epmd daemon accepts messages from both localhost and + remote hosts. However, only the query commands are answered (and + acted upon) if the query comes from a remote host. It is always an + error to try to register a nodename if the client is not a process + located on the same host as the epmd instance is running on, + why such requests are considered hostile and the connection is + immediately closed.

+ +

The queries accepted from remote nodes are:

+ + +

Port queries - i.e. on which port does the node with a given + name listen

+
+ +

Name listing - i.e. give a list of all names registered on + the host

+
+
+

To restrict access further, firewall software has to be used.

+
+
diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c index f195fdac8c..65ff0cd6b2 100644 --- a/erts/epmd/src/epmd.c +++ b/erts/epmd/src/epmd.c @@ -393,8 +393,9 @@ static void run_daemon(EpmdVars *g) static void usage(EpmdVars *g) { - fprintf(stderr, "usage: epmd [-d|-debug] [DbgExtra...] [-port No] [-daemon] [-allow_brutal_kill]\n"); - fprintf(stderr, " [-d|-debug] [-port No] [-names|-kill|-stop name]\n\n"); + fprintf(stderr, "usage: epmd [-d|-debug] [DbgExtra...] [-port No] [-daemon]\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"); @@ -406,7 +407,7 @@ 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, " get more debugging information.\n"); fprintf(stderr, " -daemon\n"); fprintf(stderr, " Start epmd detached (as a daemon)\n"); fprintf(stderr, " -relaxed_command_check\n"); @@ -415,7 +416,7 @@ static void usage(EpmdVars *g) "are registered nodes.\n"); fprintf(stderr, " Also allows forced unregister (epmd -stop).\n"); fprintf(stderr, "\nDbgExtra options\n"); - fprintf(stderr, " -packet_timout Seconds\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"); @@ -434,10 +435,10 @@ static void usage(EpmdVars *g) 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, " -allow_brutal_kill was given when epmd was started).\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 -allow_brutal_kill was given when \n"); + fprintf(stderr, " (only allowed if -relaxed_command_check was given when \n"); fprintf(stderr, " epmd was started).\n"); epmd_cleanup_exit(g,1); } -- cgit v1.2.3 From d4b6817a2785d9ce026de40dadcc948e22afffe9 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Fri, 27 Aug 2010 14:50:02 +0200 Subject: Calculate minimal packet size for ALIVE2 requests correctly --- erts/epmd/src/epmd_srv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c index 3ba8a93fbd..dbab61d6d6 100644 --- a/erts/epmd/src/epmd_srv.c +++ b/erts/epmd/src/epmd_srv.c @@ -395,7 +395,7 @@ static void do_request(g, fd, s, buf, bsize) in network byte order, and yyyyyy is symname, possibly null terminated. */ - if (bsize <= 14) /* at least one character for the node name */ + 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; -- cgit v1.2.3 From 9c1364f58f2987284ff28ab6ca19b1e01c434c8e Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Fri, 27 Aug 2010 14:51:02 +0200 Subject: Teach epmd_cli.c to not respond 'Killed' when killing denied --- erts/epmd/src/epmd_cli.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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"); -- cgit v1.2.3 From bccf8e748375559e1e4280582dc3d14446677e36 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Fri, 27 Aug 2010 15:36:07 +0200 Subject: Teach testcase epmd_SUITE:too_large to accept econnaborted --- erts/epmd/test/epmd_SUITE.erl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl index 51da934ac4..025f07380a 100644 --- a/erts/epmd/test/epmd_SUITE.erl +++ b/erts/epmd/test/epmd_SUITE.erl @@ -625,8 +625,13 @@ too_large(Config) when is_list(Config) -> 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. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -- cgit v1.2.3 From f8e74c89d0ecb15d67241e70fb4cf1d9189e6b8b Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Mon, 30 Aug 2010 12:16:45 +0200 Subject: Restore null termination of input buffer --- erts/epmd/src/epmd_srv.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c index dbab61d6d6..df4d1a5715 100644 --- a/erts/epmd/src/epmd_srv.c +++ b/erts/epmd/src/epmd_srv.c @@ -382,6 +382,9 @@ static void do_request(g, fd, s, buf, bsize) char wbuf[OUTBUF_SIZE]; /* Buffer for writing */ int i; + buf[bsize] = '\0'; /* Needed for strcmp in PORT2 and STOP requests + buf is always large enough */ + switch (*buf) { case EPMD_ALIVE2_REQ: -- cgit v1.2.3 From 72a11f23ebbbc204213c8b339e32ed6082d5ea50 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Tue, 31 Aug 2010 15:13:29 +0200 Subject: Update erl_interface doc and testsuite for epmd changes --- lib/erl_interface/doc/src/ei_connect.xml | 27 +++++++++++++++------------ lib/erl_interface/doc/src/erl_connect.xml | 29 ++++++++++++++++------------- lib/erl_interface/test/ei_accept_SUITE.erl | 4 ++-- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/lib/erl_interface/doc/src/ei_connect.xml b/lib/erl_interface/doc/src/ei_connect.xml index 927395d1bf..16439510cc 100644 --- a/lib/erl_interface/doc/src/ei_connect.xml +++ b/lib/erl_interface/doc/src/ei_connect.xml @@ -4,7 +4,7 @@
- 20012009 + 20012010 Ericsson AB. All Rights Reserved. @@ -508,7 +508,7 @@ if (ei_decode_version(result.buff, &index) < 0 same as the port number that was previously bound to the socket.

is the 32-bit IP address of the local host.

To unregister with epmd, simply close the returned - descriptor. See also .

+ descriptor. Do not use , which is deprecated anyway.

On success, the functions return a descriptor connecting the calling process to epmd. On failure, they return -1 and set to .

@@ -558,18 +558,21 @@ typedef struct { intei_unpublish(ei_cnode *ec) - Unpublish a node name + Forcefully unpublish a node name

This function can be called by a process to unregister a - specified node from epmd on the localhost. This may be - useful, for example, when epmd has not detected the failure of a - node, and will not allow the name to be reused. If you use this - function to unregister your own process, be sure to also close - the descriptor that was returned by .

- -

Careless use of this function may have unpredictable - results, if the registered node is in fact still running.

-
+ specified node from epmd on the localhost. This is however usually not + allowed, unless epmd was started with the -relaxed_command_check + flag, which it normally isn't.

+ +

To unregister a node you have published, you should + close the descriptor that was returned by + .

+ + +

This function is deprecated and will be removed in a future + release.

+

is the node structure of the node to unregister.

If the node was successfully unregistered from epmd, the function returns 0. Otherwise, it returns -1 and sets diff --git a/lib/erl_interface/doc/src/erl_connect.xml b/lib/erl_interface/doc/src/erl_connect.xml index b2235925b2..bd5e637244 100644 --- a/lib/erl_interface/doc/src/erl_connect.xml +++ b/lib/erl_interface/doc/src/erl_connect.xml @@ -4,7 +4,7 @@

- 19962009 + 19962010 Ericsson AB. All Rights Reserved. @@ -451,7 +451,7 @@ typedef struct {

is the local name to register, and should be the same as the port number that was previously bound to the socket.

To unregister with epmd, simply close the returned - descriptor. See also . + descriptor.

On success, the functions return a descriptor connecting the calling process to epmd. On failure, they return -1 and set @@ -507,21 +507,24 @@ typedef struct { interl_unpublish(alive) - Unpublish a node name + Forcefully unpublish a node name char *alive; -

This function can be called by a process to unregister a - specified node name from epmd on the localhost. This may be - useful, for example, when epmd has not detected the failure of a - node, and will not allow the name to be reused. If you use this - function to unregister your own process, be sure to also close - the descriptor that was returned by .

- -

Careless use of this function may have unpredictable - results, if the registered node is in fact still running.

-
+

This function can be called by a process to unregister a + specified node from epmd on the localhost. This is however usually not + allowed, unless epmd was started with the -relaxed_command_check + flag, which it normally isn't.

+ +

To unregister a node you have published, you should instead + close the descriptor that was returned by + .

+ + +

This function is deprecated and will be removed in a future + release.

+

is the name of the node to unregister, i.e., the first component of the nodename, without the .

If the node was successfully unregistered from epmd, the diff --git a/lib/erl_interface/test/ei_accept_SUITE.erl b/lib/erl_interface/test/ei_accept_SUITE.erl index bc83d6a62e..98fe272394 100644 --- a/lib/erl_interface/test/ei_accept_SUITE.erl +++ b/lib/erl_interface/test/ei_accept_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-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 @@ -72,7 +72,7 @@ ei_accept(Config) when is_list(Config) -> after 1000 -> io:format("timeout ~n") end, - ?line ok= ei_unpublish(P), + ?line runner:finish(P), ok. ei_threaded_accept(Config) when is_list(Config) -> -- cgit v1.2.3 From 2f2d592385b53ce594623a614c2e0cd8e2107d7d Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Wed, 1 Sep 2010 09:14:03 +0200 Subject: Teach testcases to survive TIME_WAIT overload --- erts/epmd/test/epmd_SUITE.erl | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl index 025f07380a..da69412e12 100644 --- a/erts/epmd/test/epmd_SUITE.erl +++ b/erts/epmd/test/epmd_SUITE.erl @@ -64,6 +64,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, @@ -128,8 +129,9 @@ all(suite) -> returns_valid_empty_extra, returns_valid_populated_extra_with_nulls, - buffer_overrun_1, - buffer_overrun_2, + buffer_overrun, + %buffer_overrun_1, + %buffer_overrun_2, no_nonlocal_register, no_nonlocal_kill, @@ -141,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]. @@ -723,6 +725,8 @@ 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) -> []; @@ -745,7 +749,7 @@ 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(), + {ok,E}=connect_sturdy(), gen_tcp:send(E,[<>,Bin]), closed = recv(E,1), gen_tcp:close(E), @@ -759,13 +763,13 @@ hostile2(N) -> 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(), + {ok,E}=connect_sturdy(), gen_tcp:send(E,[<>,Bin]), Z = recv(E,2), gen_tcp:close(E), (Z =:= closed) or (Z =:= {ok, [$y,1]}) catch - _:_ -> + _A:_B -> false end. @@ -932,7 +936,7 @@ 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() -> @@ -944,11 +948,15 @@ connect(Addr) -> connect_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(Addr, Port, Mode) -> - case connect_repeat(Addr, ?CONN_RETRY, Port, Mode) of + 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} -> @@ -965,16 +973,16 @@ connect(Addr, Port, Mode) -> % Try a few times before giving up. Pause a small time between % each try. -connect_repeat(Addr, 1, Port, Mode) -> +connect_repeat(Addr, 1, Port, Mode, _Sleep) -> connect_mode(Addr,Port, Mode); -connect_repeat(Addr,Retry, 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(Addr, 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) -- cgit v1.2.3