summaryrefslogtreecommitdiff
path: root/lib/erl_interface
diff options
context:
space:
mode:
authorSverker Eriksson <sverker@erlang.org>2020-01-14 15:21:38 +0100
committerSverker Eriksson <sverker@erlang.org>2020-01-14 15:31:02 +0100
commitcf6cf5e5f82e348ecb9bb02d70027fc4961aee3d (patch)
treeea7b4071477fde92e648bd2f3cbe80627214c7dd /lib/erl_interface
parent1df82a8b75d83f07f979f92a7906e30dac8ce620 (diff)
parentfc309a039a4d817725d7b9887a36d2c501a83679 (diff)
downloaderlang-cf6cf5e5f82e348ecb9bb02d70027fc4961aee3d.tar.gz
Merge 'sverker/dist-handshake/OTP-16229' into master
Diffstat (limited to 'lib/erl_interface')
-rw-r--r--lib/erl_interface/include/ei.h2
-rw-r--r--lib/erl_interface/src/connect/ei_connect.c478
-rw-r--r--lib/erl_interface/src/connect/ei_connect_int.h2
-rw-r--r--lib/erl_interface/src/epmd/ei_epmd.h7
-rw-r--r--lib/erl_interface/test/ei_accept_SUITE.erl3
-rw-r--r--lib/erl_interface/test/ei_tmo_SUITE.erl201
6 files changed, 511 insertions, 182 deletions
diff --git a/lib/erl_interface/include/ei.h b/lib/erl_interface/include/ei.h
index 93c6b3dfa7..509fa444c4 100644
--- a/lib/erl_interface/include/ei.h
+++ b/lib/erl_interface/include/ei.h
@@ -362,7 +362,7 @@ typedef struct ei_cnode_s {
/* Currently this_ipaddr isn't used */
/* struct in_addr this_ipaddr; */
char ei_connect_cookie[EI_MAX_COOKIE_SIZE+1];
- short creation;
+ unsigned int creation;
erlang_pid self;
ei_socket_callbacks *cbs;
void *setup_context;
diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c
index aab3043b9f..fee6927b95 100644
--- a/lib/erl_interface/src/connect/ei_connect.c
+++ b/lib/erl_interface/src/connect/ei_connect.c
@@ -106,7 +106,8 @@ int ei_tracelevel = 0;
(offsetof(ei_socket_callbacks, get_fd) \
+ sizeof(int (*)(void *)))
-/* FIXME why not macro? */
+typedef EI_ULONGLONG DistFlags;
+
static char *null_cookie = "";
static int get_cookie(char *buf, int len);
@@ -120,15 +121,17 @@ static int send_status(ei_socket_callbacks *cbs, void *ctx,
int pkt_sz, char *status, unsigned ms);
static int recv_status(ei_socket_callbacks *cbs, void *ctx,
int pkt_sz, unsigned ms);
-static int send_challenge(ei_socket_callbacks *cbs, void *ctx, int pkt_sz,
- char *nodename, unsigned challenge,
- unsigned version, unsigned ms);
+static int send_challenge(ei_cnode *ec, void *ctx, int pkt_sz,
+ unsigned challenge,
+ DistFlags version, unsigned ms);
static int recv_challenge(ei_socket_callbacks *cbs, void *ctx, int pkt_sz,
unsigned *challenge, unsigned *version,
- unsigned *flags, char *namebuf, unsigned ms);
+ DistFlags *flags, char *namebuf, unsigned ms);
static int send_challenge_reply(ei_socket_callbacks *cbs, void *ctx,
int pkt_sz, unsigned char digest[16],
unsigned challenge, unsigned ms);
+static int recv_complement(ei_socket_callbacks *cbs, void *ctx,
+ int pkt_sz, unsigned ms);
static int recv_challenge_reply(ei_socket_callbacks *cbs, void *ctx,
int pkt_sz, unsigned our_challenge,
char cookie[],
@@ -139,18 +142,20 @@ static int send_challenge_ack(ei_socket_callbacks *cbs, void *ctx,
static int recv_challenge_ack(ei_socket_callbacks *cbs, void *ctx,
int pkt_sz, unsigned our_challenge,
char cookie[], unsigned ms);
-static int send_name(ei_socket_callbacks *cbs, void *ctx, int pkt_sz,
- char *nodename, unsigned version, unsigned ms);
-
+static int send_name(ei_cnode *ec, void *ctx, int pkt_sz,
+ unsigned version, unsigned ms);
+static int send_complement(ei_cnode *ec, void *ctx, int pkt_sz,
+ unsigned epmd_says_version, DistFlags her_flags,
+ unsigned ms);
static int recv_name(ei_socket_callbacks *cbs, void *ctx, int pkt_sz,
- unsigned *version, unsigned *flags, char *namebuf,
- unsigned ms);
+ char* send_name_tag, DistFlags *flags,
+ char *namebuf, unsigned ms);
static int ei_connect_helper(ei_cnode* ec,
Erl_IpAddr ip_addr,
char *alivename,
unsigned ms,
int rport,
- int dist);
+ int epmd_says_version);
static struct hostent*
dyn_gethostbyname_r(const char *name, struct hostent *hostp, char **buffer_p,
@@ -956,12 +961,13 @@ static int ei_connect_helper(ei_cnode* ec,
char *alivename,
unsigned ms,
int rport,
- int dist)
+ int epmd_says_version)
{
ei_socket_callbacks *cbs = ec->cbs;
void *ctx;
int sockd;
- unsigned her_flags, her_version;
+ unsigned her_version;
+ DistFlags her_flags;
unsigned our_challenge, her_challenge;
unsigned char our_digest[16];
int err;
@@ -979,8 +985,9 @@ static int ei_connect_helper(ei_cnode* ec,
rport);
}
- if (dist <= 4) {
- EI_TRACE_ERR0("ei_xconnect","-> CONNECT remote version not compatible");
+ if (epmd_says_version < EI_DIST_LOW) {
+ EI_TRACE_ERR1("ei_xconnect","-> CONNECT remote version %d not compatible",
+ epmd_says_version);
return ERL_ERROR;
}
@@ -1020,21 +1027,24 @@ static int ei_connect_helper(ei_cnode* ec,
goto error;
}
- if (send_name(cbs, ctx, pkt_sz, ec->thisnodename, (unsigned) dist, tmo))
+ if (send_name(ec, ctx, pkt_sz, epmd_says_version, tmo))
goto error;
if (recv_status(cbs, ctx, pkt_sz, tmo))
goto error;
- if (recv_challenge(cbs, ctx, pkt_sz, &her_challenge,
- &her_version, &her_flags, NULL, tmo))
+ if (recv_challenge(cbs, ctx, pkt_sz, &her_challenge, &her_version,
+ &her_flags, NULL, tmo))
goto error;
+ her_version = (her_flags & DFLAG_HANDSHAKE_23) ? EI_DIST_6 : EI_DIST_5;
our_challenge = gen_challenge();
gen_digest(her_challenge, ec->ei_connect_cookie, our_digest);
+ if (send_complement(ec, ctx, pkt_sz, epmd_says_version, her_flags, tmo))
+ goto error;
if (send_challenge_reply(cbs, ctx, pkt_sz, our_digest, our_challenge, tmo))
goto error;
if (recv_challenge_ack(cbs, ctx, pkt_sz, our_challenge,
ec->ei_connect_cookie, tmo))
goto error;
- if (put_ei_socket_info(sockd, dist, null_cookie, ec, cbs, ctx) != 0)
+ if (put_ei_socket_info(sockd, her_version, null_cookie, ec, cbs, ctx) != 0)
goto error;
if (cbs->connect_handshake_complete) {
@@ -1134,15 +1144,15 @@ int ei_connect_host_port(ei_cnode* ec, char *host, int port)
int ei_xconnect_tmo(ei_cnode* ec, Erl_IpAddr ip_addr, char *alivename, unsigned ms)
{
- int dist = 0;
+ int epmd_says_version = 0;
int port;
unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms;
- if ((port = ei_epmd_port_tmo(ip_addr,alivename,&dist, tmo)) < 0) {
+ if ((port = ei_epmd_port_tmo(ip_addr,alivename,&epmd_says_version, tmo)) < 0) {
EI_TRACE_ERR0("ei_xconnect","-> CONNECT can't get remote port");
/* ei_epmd_port_tmo() has set erl_errno */
return ERL_NO_PORT;
}
- return ei_connect_helper(ec, ip_addr, alivename, ms, port, dist);
+ return ei_connect_helper(ec, ip_addr, alivename, ms, port, epmd_says_version);
}
int ei_xconnect(ei_cnode* ec, Erl_IpAddr ip_addr, char *alivename)
@@ -1152,7 +1162,7 @@ int ei_xconnect(ei_cnode* ec, Erl_IpAddr ip_addr, char *alivename)
int ei_xconnect_host_port_tmo(ei_cnode* ec, Erl_IpAddr ip_addr, int port, unsigned ms)
{
- return ei_connect_helper(ec, ip_addr, NULL, ms, port, 5);
+ return ei_connect_helper(ec, ip_addr, NULL, ms, port, EI_DIST_LOW);
}
int ei_xconnect_host_port(ei_cnode* ec, Erl_IpAddr ip_addr, int port)
@@ -1275,8 +1285,9 @@ int ei_accept(ei_cnode* ec, int lfd, ErlConnect *conp)
int ei_accept_tmo(ei_cnode* ec, int lfd, ErlConnect *conp, unsigned ms)
{
int fd;
- unsigned her_version, her_flags;
+ DistFlags her_flags;
char tmp_nodename[MAXNODELEN+1];
+ char send_name_tag;
char *her_name;
int pkt_sz, err;
struct sockaddr_in addr;
@@ -1301,6 +1312,10 @@ int ei_accept_tmo(ei_cnode* ec, int lfd, ErlConnect *conp, unsigned ms)
ctx = EI_FD_AS_CTX__(lfd);
}
+ if (ec->cbs != cbs) {
+ EI_CONN_SAVE_ERRNO__(EINVAL);
+ return ERL_ERROR;
+ }
EI_TRACE_CONN0("ei_accept","<- ACCEPT waiting for connection");
@@ -1347,16 +1362,14 @@ int ei_accept_tmo(ei_cnode* ec, int lfd, ErlConnect *conp, unsigned ms)
EI_TRACE_CONN0("ei_accept","<- ACCEPT connected to remote");
- if (recv_name(cbs, ctx, pkt_sz, &her_version, &her_flags, her_name, tmo)) {
+ if (recv_name(cbs, ctx, pkt_sz, &send_name_tag, &her_flags,
+ her_name, tmo)) {
EI_TRACE_ERR0("ei_accept","<- ACCEPT initial ident failed");
goto error;
}
- if (her_version <= 4) {
- EI_TRACE_ERR0("ei_accept","<- ACCEPT remote version not compatible");
- goto error;
- }
- else {
+ {
+ unsigned her_version = (her_flags & DFLAG_HANDSHAKE_23) ? 6 : 5;
unsigned our_challenge;
unsigned her_challenge;
unsigned char our_digest[16];
@@ -1364,9 +1377,12 @@ int ei_accept_tmo(ei_cnode* ec, int lfd, ErlConnect *conp, unsigned ms)
if (send_status(cbs, ctx, pkt_sz, "ok", tmo))
goto error;
our_challenge = gen_challenge();
- if (send_challenge(cbs, ctx, pkt_sz, ec->thisnodename,
- our_challenge, her_version, tmo))
+ if (send_challenge(ec, ctx, pkt_sz, our_challenge, her_flags, tmo))
goto error;
+ if (send_name_tag == 'n' && (her_flags & DFLAG_HANDSHAKE_23)) {
+ if (recv_complement(cbs, ctx, pkt_sz, tmo))
+ goto error;
+ }
if (recv_challenge_reply(cbs, ctx, pkt_sz, our_challenge,
ec->ei_connect_cookie, &her_challenge, tmo))
goto error;
@@ -1912,26 +1928,50 @@ error:
return -1;
}
-static int send_name_or_challenge(ei_socket_callbacks *cbs,
- void *ctx,
- int pkt_sz,
- char *nodename,
- int f_chall,
- unsigned challenge,
- unsigned version,
- unsigned ms)
+static DistFlags preferred_flags(void)
+{
+ DistFlags flags =
+ DFLAG_EXTENDED_REFERENCES
+ | DFLAG_DIST_MONITOR
+ | DFLAG_EXTENDED_PIDS_PORTS
+ | DFLAG_FUN_TAGS
+ | DFLAG_NEW_FUN_TAGS
+ | DFLAG_NEW_FLOATS
+ | DFLAG_SMALL_ATOM_TAGS
+ | DFLAG_UTF8_ATOMS
+ | DFLAG_MAP_TAG
+ | DFLAG_BIG_CREATION
+ | DFLAG_EXPORT_PTR_TAG
+ | DFLAG_BIT_BINARIES
+ | DFLAG_HANDSHAKE_23;
+ if (ei_internal_use_21_bitstr_expfun()) {
+ flags &= ~(DFLAG_EXPORT_PTR_TAG
+ | DFLAG_BIT_BINARIES);
+ }
+ return flags;
+}
+
+static int send_name(ei_cnode *ec,
+ void *ctx,
+ int pkt_sz,
+ unsigned version,
+ unsigned ms)
{
char *buf;
unsigned char *s;
char dbuf[DEFBUF_SIZ];
- int siz = pkt_sz + 1 + 2 + 4 + strlen(nodename);
- const char* function[] = {"SEND_NAME", "SEND_CHALLENGE"};
+ const unsigned int nodename_len = strlen(ec->thisnodename);
+ int siz;
int err;
ssize_t len;
- unsigned int flags;
+ DistFlags flags;
+ const char tag = (version == EI_DIST_5) ? 'n' : 'N';
+
+ if (tag == 'n')
+ siz = pkt_sz + 1 + 2 + 4 + nodename_len;
+ else
+ siz = pkt_sz + 1 + 8 + 4 + 2 + nodename_len;
- if (f_chall)
- siz += 4;
buf = (siz > DEFBUF_SIZ) ? malloc(siz) : dbuf;
if (!buf) {
erl_errno = ENOMEM;
@@ -1948,35 +1988,95 @@ static int send_name_or_challenge(ei_socket_callbacks *cbs,
default:
return -1;
}
- put8(s, 'n');
- put16be(s, version);
- flags = (DFLAG_EXTENDED_REFERENCES
- | DFLAG_DIST_MONITOR
- | DFLAG_EXTENDED_PIDS_PORTS
- | DFLAG_FUN_TAGS
- | DFLAG_NEW_FUN_TAGS
- | DFLAG_NEW_FLOATS
- | DFLAG_SMALL_ATOM_TAGS
- | DFLAG_UTF8_ATOMS
- | DFLAG_MAP_TAG
- | DFLAG_BIG_CREATION
- | DFLAG_EXPORT_PTR_TAG
- | DFLAG_BIT_BINARIES);
- if (ei_internal_use_21_bitstr_expfun()) {
- flags &= ~(DFLAG_EXPORT_PTR_TAG
- | DFLAG_BIT_BINARIES);
+ flags = preferred_flags();
+
+ put8(s, tag);
+ if (tag == 'n') {
+ put16be(s, EI_DIST_5); /* some impl (jinterface) demand ver==5 */
+ put32be(s, flags);
+ }
+ else { /* tag == 'N' */
+ put64be(s, flags);
+ put32be(s, ec->creation);
+ put16be(s, nodename_len);
}
- put32be(s, flags);
- if (f_chall)
- put32be(s, challenge);
- memcpy(s, nodename, strlen(nodename));
+ memcpy(s, ec->thisnodename, nodename_len);
len = (ssize_t) siz;
- err = ei_write_fill_ctx_t__(cbs, ctx, buf, &len, ms);
+ err = ei_write_fill_ctx_t__(ec->cbs, ctx, buf, &len, ms);
if (!err && len != (ssize_t) siz)
err = EIO;
if (err) {
- EI_TRACE_ERR1("send_name_or_challenge",
- "-> %s socket write failed", function[f_chall]);
+ EI_TRACE_ERR0("send_name", "SEND_NAME -> socket write failed");
+ if (buf != dbuf)
+ free(buf);
+ EI_CONN_SAVE_ERRNO__(err);
+ return -1;
+ }
+
+ if (buf != dbuf)
+ free(buf);
+ return 0;
+}
+
+static int send_challenge(ei_cnode *ec,
+ void *ctx,
+ int pkt_sz,
+ unsigned challenge,
+ DistFlags her_flags,
+ unsigned ms)
+{
+ char *buf;
+ unsigned char *s;
+ char dbuf[DEFBUF_SIZ];
+ const unsigned int nodename_len = strlen(ec->thisnodename);
+ int siz;
+ int err;
+ ssize_t len;
+ DistFlags flags;
+ const char tag = (her_flags & DFLAG_HANDSHAKE_23) ? 'N' : 'n';
+
+ if (tag == 'n')
+ siz = pkt_sz + 1 + 2 + 4 + 4 + nodename_len;
+ else
+ siz = pkt_sz + 1 + 8 + 4 + 4 + 2 + nodename_len;
+
+ buf = (siz > DEFBUF_SIZ) ? malloc(siz) : dbuf;
+ if (!buf) {
+ erl_errno = ENOMEM;
+ return -1;
+ }
+ s = (unsigned char *)buf;
+ switch (pkt_sz) {
+ case 2:
+ put16be(s,siz - 2);
+ break;
+ case 4:
+ put32be(s,siz - 4);
+ break;
+ default:
+ return -1;
+ }
+
+ flags = preferred_flags();
+ put8(s, tag);
+ if (tag == 'n') {
+ put16be(s, EI_DIST_5); /* choosen version */
+ put32be(s, flags);
+ put32be(s, challenge);
+ }
+ else {
+ put64be(s, flags);
+ put32be(s, challenge);
+ put32be(s, ec->creation);
+ put16be(s, nodename_len);
+ }
+ memcpy(s, ec->thisnodename, nodename_len);
+ len = (ssize_t) siz;
+ err = ei_write_fill_ctx_t__(ec->cbs, ctx, buf, &len, ms);
+ if (!err && len != (ssize_t) siz)
+ err = EIO;
+ if (err) {
+ EI_TRACE_ERR0("send_challenge", "-> SEND_CHALLENGE socket write failed");
if (buf != dbuf)
free(buf);
EI_CONN_SAVE_ERRNO__(err);
@@ -1990,13 +2090,13 @@ static int send_name_or_challenge(ei_socket_callbacks *cbs,
static int recv_challenge(ei_socket_callbacks *cbs, void *ctx,
int pkt_sz, unsigned *challenge, unsigned *version,
- unsigned *flags, char *namebuf, unsigned ms)
+ DistFlags *flags, char *namebuf, unsigned ms)
{
char dbuf[DEFBUF_SIZ];
char *buf = dbuf;
int is_static = 1;
int buflen = DEFBUF_SIZ;
- int rlen;
+ int rlen, nodename_len;
char *s;
char tag;
char tmp_nodename[MAXNODELEN+1];
@@ -2009,21 +2109,57 @@ static int recv_challenge(ei_socket_callbacks *cbs, void *ctx,
"<- RECV_CHALLENGE socket read failed (%d)",rlen);
goto error;
}
- if ((rlen - 11) > MAXNODELEN) {
- EI_TRACE_ERR1("recv_challenge",
- "<- RECV_CHALLENGE nodename too long (%d)",rlen - 11);
- goto error;
- }
s = buf;
- if ((tag = get8(s)) != 'n') {
+ tag = get8(s);
+ if (tag != 'n' && tag != 'N') {
EI_TRACE_ERR2("recv_challenge",
"<- RECV_CHALLENGE incorrect tag, "
- "expected 'n' got '%c' (%u)",tag,tag);
+ "expected 'n' or 'N', got '%c' (%u)",tag,tag);
goto error;
}
- *version = get16be(s);
- *flags = get32be(s);
- *challenge = get32be(s);
+ if (tag == 'n') { /* OLD */
+ unsigned int version;
+ if (rlen < 1+2+4+4) {
+ EI_TRACE_ERR1("recv_challenge","<- RECV_CHALLENGE 'n' packet too short (%d)",
+ rlen)
+ goto error;
+ }
+
+ version = get16be(s);
+ if (version != EI_DIST_5) {
+ EI_TRACE_ERR1("recv_challenge",
+ "<- RECV_CHALLENGE 'n' incorrect version=%d",
+ version);
+ goto error;
+ }
+ *flags = get32be(s);
+ *challenge = get32be(s);
+ nodename_len = (buf + rlen) - s;
+ }
+ else { /* NEW */
+ if (rlen < 1+8+4+4+2) {
+ EI_TRACE_ERR1("recv_challenge","<- RECV_CHALLENGE 'N' packet too short (%d)",
+ rlen)
+ goto error;
+ }
+ *version = EI_DIST_6;
+ *flags = get64be(s);
+ *challenge = get32be(s);
+ s += 4; /* ignore peer 'creation' */
+ nodename_len = get16be(s);
+ if (nodename_len > (buf + rlen) - s) {
+ EI_TRACE_ERR1("recv_challenge",
+ "<- RECV_CHALLENGE 'N' nodename too long (%d)",
+ nodename_len);
+ goto error;
+ }
+ }
+
+ if (nodename_len > MAXNODELEN) {
+ EI_TRACE_ERR1("recv_challenge",
+ "<- RECV_CHALLENGE nodename too long (%d)", nodename_len);
+ goto error;
+ }
if (!(*flags & DFLAG_EXTENDED_REFERENCES)) {
EI_TRACE_ERR0("recv_challenge","<- RECV_CHALLENGE peer cannot "
@@ -2047,8 +2183,8 @@ static int recv_challenge(ei_socket_callbacks *cbs, void *ctx,
if (!namebuf)
namebuf = &tmp_nodename[0];
- memcpy(namebuf, s, rlen - 11);
- namebuf[rlen - 11] = '\0';
+ memcpy(namebuf, s, nodename_len);
+ namebuf[nodename_len] = '\0';
if (!is_static)
free(buf);
@@ -2069,6 +2205,63 @@ error:
return -1;
}
+static int send_complement(ei_cnode *ec,
+ void *ctx,
+ int pkt_sz,
+ unsigned epmd_says_version,
+ DistFlags her_flags,
+ unsigned ms)
+{
+ if (epmd_says_version == EI_DIST_5 && (her_flags & DFLAG_HANDSHAKE_23)) {
+ char *buf;
+ unsigned char *s;
+ char dbuf[DEFBUF_SIZ];
+ int err;
+ ssize_t len;
+ unsigned int flagsHigh;
+ const int siz = pkt_sz + 1 + 4 + 4;
+
+ buf = (siz > DEFBUF_SIZ) ? malloc(siz) : dbuf;
+ if (!buf) {
+ erl_errno = ENOMEM;
+ return -1;
+ }
+ s = (unsigned char *)buf;
+ switch (pkt_sz) {
+ case 2:
+ put16be(s,siz - 2);
+ break;
+ case 4:
+ put32be(s,siz - 4);
+ break;
+ default:
+ return -1;
+ }
+ flagsHigh = preferred_flags() >> 32;
+
+ put8(s, 'c');
+ put32be(s, flagsHigh);
+ put32be(s, ec->creation);
+
+ len = (ssize_t) siz;
+ err = ei_write_fill_ctx_t__(ec->cbs, ctx, buf, &len, ms);
+ if (!err && len != (ssize_t) siz)
+ err = EIO;
+ if (err) {
+ EI_TRACE_ERR0("send_name", "SEND_NAME -> socket write failed");
+ if (buf != dbuf)
+ free(buf);
+ EI_CONN_SAVE_ERRNO__(err);
+ return -1;
+ }
+
+ if (buf != dbuf)
+ free(buf);
+ }
+ return 0;
+}
+
+
static int send_challenge_reply(ei_socket_callbacks *cbs, void *ctx,
int pkt_sz, unsigned char digest[16],
unsigned challenge, unsigned ms)
@@ -2115,6 +2308,54 @@ static int send_challenge_reply(ei_socket_callbacks *cbs, void *ctx,
return 0;
}
+static int recv_complement(ei_socket_callbacks *cbs,
+ void *ctx,
+ int pkt_sz,
+ unsigned ms)
+{
+ char dbuf[DEFBUF_SIZ];
+ char *buf = dbuf;
+ int is_static = 1;
+ int buflen = DEFBUF_SIZ;
+ int rlen;
+ char *s;
+ char tag;
+ unsigned int creation;
+
+ erl_errno = EIO; /* Default */
+
+ if ((rlen = read_hs_package(cbs, ctx, pkt_sz, &buf, &buflen, &is_static, ms)) != 21) {
+ EI_TRACE_ERR1("recv_complement",
+ "<- RECV_COMPLEMENT socket read failed (%d)",rlen);
+ goto error;
+ }
+
+ s = buf;
+ if ((tag = get8(s)) != 'c') {
+ EI_TRACE_ERR2("recv_complement",
+ "<- RECV_COMPLEMENT incorrect tag, "
+ "expected 'c' got '%c' (%u)",tag,tag);
+ goto error;
+ }
+ creation = get32be(s);
+ if (!is_static)
+ free(buf);
+
+ if (ei_tracelevel >= 3) {
+ EI_TRACE_CONN1("recv_complement",
+ "<- RECV_COMPLEMENT (ok) creation = %u",
+ creation);
+ }
+ /* We don't have any use for 'creation' of other node, so we drop it */
+ erl_errno = 0;
+ return 0;
+
+error:
+ if (!is_static)
+ free(buf);
+ return -1;
+}
+
static int recv_challenge_reply(ei_socket_callbacks *cbs,
void *ctx,
int pkt_sz,
@@ -2270,30 +2511,16 @@ error:
return -1;
}
-static int send_name(ei_socket_callbacks *cbs, void *ctx, int pkt_sz,
- char *nodename, unsigned version, unsigned ms)
-{
- return send_name_or_challenge(cbs, ctx, pkt_sz, nodename, 0,
- 0, version, ms);
-}
-
-static int send_challenge(ei_socket_callbacks *cbs, void *ctx, int pkt_sz,
- char *nodename, unsigned challenge, unsigned version,
- unsigned ms)
-{
- return send_name_or_challenge(cbs, ctx, pkt_sz, nodename, 1,
- challenge, version, ms);
-}
-
static int recv_name(ei_socket_callbacks *cbs, void *ctx,
- int pkt_sz, unsigned *version,
- unsigned *flags, char *namebuf, unsigned ms)
+ int pkt_sz, char *send_name_tag,
+ DistFlags *flags, char *namebuf, unsigned ms)
{
char dbuf[DEFBUF_SIZ];
char *buf = dbuf;
int is_static = 1;
int buflen = DEFBUF_SIZ;
int rlen;
+ unsigned int namelen;
char *s;
char tmp_nodename[MAXNODELEN+1];
char tag;
@@ -2305,19 +2532,40 @@ static int recv_name(ei_socket_callbacks *cbs, void *ctx,
EI_TRACE_ERR1("recv_name","<- RECV_NAME socket read failed (%d)",rlen);
goto error;
}
- if ((rlen - 7) > MAXNODELEN) {
- EI_TRACE_ERR1("recv_name","<- RECV_NAME nodename too long (%d)",rlen-7);
- goto error;
- }
s = buf;
tag = get8(s);
- if (tag != 'n') {
+ *send_name_tag = tag;
+ if (tag != 'n' && tag != 'N') {
EI_TRACE_ERR2("recv_name","<- RECV_NAME incorrect tag, "
- "expected 'n' got '%c' (%u)",tag,tag);
+ "expected 'n' or 'N', got '%c' (%u)",tag,tag);
goto error;
}
- *version = get16be(s);
- *flags = get32be(s);
+ if (tag == 'n') {
+ unsigned int version;
+ if (rlen < 1+2+4) {
+ EI_TRACE_ERR1("recv_name","<- RECV_NAME 'n' packet too short (%d)",
+ rlen)
+ goto error;
+ }
+ version = get16be(s);
+ if (version < EI_DIST_5) {
+ EI_TRACE_ERR1("recv_name","<- RECV_NAME 'n' invalid version=%d",
+ version)
+ goto error;
+ }
+ *flags = get32be(s);
+ namelen = rlen - (1+2+4);
+ }
+ else { /* tag == 'N' */
+ if (rlen < 1+8+4+2) {
+ EI_TRACE_ERR1("recv_name","<- RECV_NAME 'N' packet too short (%d)",
+ rlen)
+ goto error;
+ }
+ *flags = get64be(s);
+ s += 4; /* ignore peer 'creation' */
+ namelen = get16be(s);
+ }
if (!(*flags & DFLAG_EXTENDED_REFERENCES)) {
EI_TRACE_ERR0("recv_name","<- RECV_NAME peer cannot handle"
@@ -2335,14 +2583,20 @@ static int recv_name(ei_socket_callbacks *cbs, void *ctx,
if (!namebuf)
namebuf = &tmp_nodename[0];
- memcpy(namebuf, s, rlen - 7);
- namebuf[rlen - 7] = '\0';
+ if (namelen > MAXNODELEN || s+namelen > buf+rlen) {
+ EI_TRACE_ERR2("recv_name","<- RECV_NAME '%c' nodename too long (%d)",
+ tag, namelen);
+ goto error;
+ }
+
+ memcpy(namebuf, s, namelen);
+ namebuf[namelen] = '\0';
if (!is_static)
free(buf);
EI_TRACE_CONN3("recv_name",
- "<- RECV_NAME (ok) node = %s, version = %u, flags = %u",
- namebuf,*version,*flags);
+ "<- RECV_NAME (ok) node = %s, tag = %c, flags = %u",
+ namebuf,tag,*flags);
erl_errno = 0;
return 0;
diff --git a/lib/erl_interface/src/connect/ei_connect_int.h b/lib/erl_interface/src/connect/ei_connect_int.h
index b41a5f2b23..428713e015 100644
--- a/lib/erl_interface/src/connect/ei_connect_int.h
+++ b/lib/erl_interface/src/connect/ei_connect_int.h
@@ -109,6 +109,8 @@ extern int h_errno;
#define DFLAG_UTF8_ATOMS 0x10000
#define DFLAG_MAP_TAG 0x20000
#define DFLAG_BIG_CREATION 0x40000
+#define DFLAG_HANDSHAKE_23 0x1000000
+#define DFLAG_HANDSHAKE_XX 0xfe000000 /* bits reserved for handshake changes */
ei_cnode *ei_fd_to_cnode(int fd);
int ei_distversion(int fd);
diff --git a/lib/erl_interface/src/epmd/ei_epmd.h b/lib/erl_interface/src/epmd/ei_epmd.h
index 597a955676..e3cb041dc9 100644
--- a/lib/erl_interface/src/epmd/ei_epmd.h
+++ b/lib/erl_interface/src/epmd/ei_epmd.h
@@ -24,9 +24,12 @@
#define INADDR_LOOPBACK ((u_long) 0x7F000001)
#endif
+#define EI_DIST_5 5 /* OTP R4 - 22 */
+#define EI_DIST_6 6 /* OTP 23 and later */
+
#ifndef EI_DIST_HIGH
-#define EI_DIST_HIGH 6 /* OTP 23 and later */
-#define EI_DIST_LOW 5 /* OTP R4 - 22 */
+#define EI_DIST_HIGH EI_DIST_6
+#define EI_DIST_LOW EI_DIST_5
#endif
#ifndef EPMD_PORT
diff --git a/lib/erl_interface/test/ei_accept_SUITE.erl b/lib/erl_interface/test/ei_accept_SUITE.erl
index ceb1e401ff..c49b8a358a 100644
--- a/lib/erl_interface/test/ei_accept_SUITE.erl
+++ b/lib/erl_interface/test/ei_accept_SUITE.erl
@@ -75,7 +75,8 @@ ei_accept_do(Config, CompatRel, SockImpl) ->
{ok, ListenFd} = ei_publish(P, Port),
{any, EINode} ! TermToSend,
- {ok, Fd, _Node} = ei_accept(P, ListenFd),
+ {ok, Fd, Node} = ei_accept(P, ListenFd),
+ Node = node(),
Got1 = ei_receive(P, Fd),
%% Send again, now without auto-connect
diff --git a/lib/erl_interface/test/ei_tmo_SUITE.erl b/lib/erl_interface/test/ei_tmo_SUITE.erl
index cbb3f2cf30..8d8776949c 100644
--- a/lib/erl_interface/test/ei_tmo_SUITE.erl
+++ b/lib/erl_interface/test/ei_tmo_SUITE.erl
@@ -106,6 +106,8 @@ do_one_recv_failure(Config,CNode) ->
true = (Ret < 0),
runner:recv_eot(P1).
+-define(EI_DIST_LOW, 5).
+-define(EI_DIST_HIGH, 6).
%% Check send with timeouts.
ei_send_tmo(Config) when is_list(Config) ->
@@ -138,11 +140,15 @@ do_one_send(Config,From,CNode) ->
ei_send_failure_tmo(Config) when is_list(Config) ->
register(ei_send_tmo_1,self()),
- do_one_send_failure(Config,self(),cccc1,c_nod_send_tmo_3),
- do_one_send_failure(Config,ei_send_tmo_1,cccc2,c_nod_send_tmo_4),
+ [begin
+ io:format("Test dist version ~p\n", [Ver]),
+ do_one_send_failure(Config,self(),cccc1,c_nod_send_tmo_3, Ver),
+ do_one_send_failure(Config,ei_send_tmo_1,cccc2,c_nod_send_tmo_4, Ver)
+ end
+ || Ver <- lists:seq(?EI_DIST_LOW, ?EI_DIST_HIGH)],
ok.
-do_one_send_failure(Config,From,FakeName,CName) ->
+do_one_send_failure(Config,From,FakeName,CName, OurVer) ->
{_,Host} = split(node()),
OurName = join(FakeName,Host),
Node = join(CName,Host),
@@ -152,7 +158,7 @@ do_one_send_failure(Config,From,FakeName,CName) ->
Else ->
exit(Else)
end,
- EpmdSocket = register(OurName, LSocket, 1, 5),
+ EpmdSocket = epmd_register(OurName, LSocket, OurVer),
P3 = runner:start(Config, ?send_tmo),
Cookie = kaksmula_som_ingen_bryr_sig_om,
runner:send_term(P3,{CName,
@@ -165,10 +171,10 @@ do_one_send_failure(Config,From,FakeName,CName) ->
Else2 ->
exit(Else2)
end,
- {hidden,Node,5} = recv_name(SocketB), % See 1)
+ {hidden,Node} = recv_name(SocketB, OurVer), % See 1)
send_status(SocketB, ok),
MyChallengeB = gen_challenge(),
- send_challenge(SocketB, OurName, MyChallengeB, 5),
+ send_challenge(SocketB, OurName, MyChallengeB, OurVer),
HisChallengeB = recv_challenge_reply(SocketB,
MyChallengeB,
Cookie),
@@ -214,6 +220,15 @@ ei_connect_unreachable_tmo(Config) when is_list(Config) ->
ok.
ei_connect_tmo(Config) when is_list(Config) ->
+ [begin
+ io:format("Test dist version ~p published as ~p\n", [OurVer,OurEpmdVer]),
+ do_ei_connect_tmo(Config, OurVer, OurEpmdVer)
+ end
+ || OurVer <- lists:seq(?EI_DIST_LOW, ?EI_DIST_HIGH),
+ OurEpmdVer <- lists:seq(?EI_DIST_LOW, ?EI_DIST_HIGH),
+ OurVer >= OurEpmdVer].
+
+do_ei_connect_tmo(Config, OurVer, OurEpmdVer) ->
P2 = runner:start(Config, ?connect_tmo),
runner:send_term(P2,{c_nod_connect_tmo_2,
erlang:get_cookie(),
@@ -232,7 +247,7 @@ ei_connect_tmo(Config) when is_list(Config) ->
Else ->
exit(Else)
end,
- EpmdSocket = register(OurName, LSocket, 1, 5),
+ EpmdSocket = epmd_register(OurName, LSocket, OurEpmdVer),
P3 = runner:start(Config, ?connect_tmo),
Cookie = kaksmula_som_ingen_bryr_sig_om,
runner:send_term(P3,{c_nod_connect_tmo_3,
@@ -245,10 +260,11 @@ ei_connect_tmo(Config) when is_list(Config) ->
Else2 ->
exit(Else2)
end,
- {hidden,Node,5} = recv_name(SocketB), % See 1)
+ {hidden,Node} = recv_name(SocketB, OurEpmdVer), % See 1)
send_status(SocketB, ok),
MyChallengeB = gen_challenge(),
- send_challenge(SocketB, OurName, MyChallengeB, 5),
+ send_challenge(SocketB, OurName, MyChallengeB, OurVer),
+ recv_complement(SocketB, OurVer, OurEpmdVer),
_HisChallengeB = recv_challenge_reply(SocketB,
MyChallengeB,
Cookie),
@@ -261,8 +277,17 @@ ei_connect_tmo(Config) when is_list(Config) ->
%% Check accept with timeouts.
ei_accept_tmo(Config) when is_list(Config) ->
- %%dbg:tracer(),
- %%dbg:p(self()),
+ [begin
+ io:format("Test our dist ver=~p and assumed ver=~p\n",
+ [OurVer, AssumedVer]),
+ do_ei_accept_tmo(Config, OurVer, AssumedVer)
+ end
+ || OurVer <- lists:seq(?EI_DIST_LOW, ?EI_DIST_HIGH),
+ AssumedVer <- lists:seq(?EI_DIST_LOW, ?EI_DIST_HIGH),
+ OurVer >= AssumedVer],
+ ok.
+
+do_ei_accept_tmo(Config, OurVer, AssumedVer) ->
P = runner:start(Config, ?accept_tmo),
runner:send_term(P,{c_nod_som_ingen_kontaktar_1,
kaksmula_som_ingen_bryr_sig_om,
@@ -288,13 +313,13 @@ ei_accept_tmo(Config) when is_list(Config) ->
{NA,NB} = split(CNode2),
{_,Host} = split(node()),
OurName = join(ccc,Host),
- {port,PortNo,_} = erl_epmd:port_please(NA,NB),
+ {port,PortNo,?EI_DIST_HIGH} = erl_epmd:port_please(NA,NB),
{ok, SocketA} = gen_tcp:connect(atom_to_list(NB),PortNo,
[{active,false},
{packet,2}]),
- send_name(SocketA,OurName,5),
+ send_name(SocketA,OurName,OurVer,AssumedVer),
ok = recv_status(SocketA),
- {hidden,_Node,5,HisChallengeA} = recv_challenge(SocketA), % See 1)
+ {hidden,_Node,HisChallengeA} = recv_challenge(SocketA,OurVer), % See 1)
_OurChallengeA = gen_challenge(),
_OurDigestA = gen_digest(HisChallengeA, erlang:get_cookie()),
%% Dont do the last two steps of the connection setup...
@@ -340,6 +365,7 @@ make_and_check_dummy() ->
-define(DFLAG_EXTENDED_PIDS_PORTS,16#100).
-define(DFLAG_NEW_FLOATS,16#800).
-define(DFLAG_DIST_MONITOR,8).
+-define(DFLAG_HANDSHAKE_23,16#1000000).
%% From R9 and forward extended references is compulsory
%% From 14 and forward new float is compulsory
@@ -403,31 +429,61 @@ recv_status(Socket) ->
exit(Bad)
end.
-send_challenge(Socket, Node, Challenge, Version) ->
- send_challenge(Socket, Node, Challenge, Version, ?COMPULSORY_DFLAGS).
-send_challenge(Socket, Node, Challenge, Version, Flags) ->
- {ok, {{_Ip1,_Ip2,_Ip3,_Ip4}, _}} = inet:sockname(Socket),
- ?to_port(Socket, [$n,?int16(Version),?int32(Flags),
- ?int32(Challenge), atom_to_list(Node)]).
-
-recv_challenge(Socket) ->
+send_challenge(Socket, Node, Challenge, OurVer) ->
+ send_challenge(Socket, Node, Challenge, OurVer, ?COMPULSORY_DFLAGS).
+
+send_challenge(Socket, Node, Challenge, OurVer, Flags) ->
+ if OurVer =:= 5 ->
+ ?to_port(Socket, [$n, ?int16(OurVer), ?int32(Flags),
+ ?int32(Challenge), atom_to_list(Node)]);
+ OurVer >= 6 ->
+ NodeName = atom_to_binary(Node, latin1),
+ NameLen = byte_size(NodeName),
+ Creation = erts_internal:get_creation(),
+ ?to_port(Socket, [$N,
+ <<(Flags bor ?DFLAG_HANDSHAKE_23):64,
+ Challenge:32,
+ Creation:32,
+ NameLen:16>>,
+ NodeName])
+ end.
+
+recv_challenge(Socket, OurVer) ->
case gen_tcp:recv(Socket, 0) of
{ok,[$n,V1,V0,Fl1,Fl2,Fl3,Fl4,CA3,CA2,CA1,CA0 | Ns]} ->
+ 5 = OurVer,
Flags = ?u32(Fl1,Fl2,Fl3,Fl4),
- Type = case Flags band ?DFLAG_PUBLISHED of
- 0 ->
- hidden;
- _ ->
- normal
- end,
+ Type = flags_to_type(Flags),
Node =list_to_atom(Ns),
- Version = ?u16(V1,V0),
+ OurVer = ?u16(V1,V0), % echoed back
+ Challenge = ?u32(CA3,CA2,CA1,CA0),
+ {Type,Node,Challenge};
+
+ {ok,[$N, F7,F6,F5,F4,F3,F2,F1,F0, CA3,CA2,CA1,CA0,
+ _Cr3,_Cr2,_Cr1,_Cr0, NL1,NL0 | Rest]} ->
+ true = (OurVer >= 6),
+ <<Flags:64>> = <<F7,F6,F5,F4,F3,F2,F1,F0>>,
+ Type = flags_to_type(Flags),
+ NameLen = ?u16(NL1,NL0),
+ {NodeName,_} = lists:split(NameLen, Rest),
+ Node = list_to_atom(NodeName),
Challenge = ?u32(CA3,CA2,CA1,CA0),
- {Type,Node,Version,Challenge};
+ %%Creation = ?u32(Cr3,Cr2,Cr1,Cr0),
+ %%true = (Creation =/= 0),
+ {Type,Node,Challenge};
+
_ ->
?shutdown(no_node)
end.
+flags_to_type(Flags) ->
+ case Flags band ?DFLAG_PUBLISHED of
+ 0 ->
+ hidden;
+ _ ->
+ normal
+ end.
+
%send_challenge_reply(Socket, Challenge, Digest) ->
% ?to_port(Socket, [$r,?int32(Challenge),Digest]).
@@ -441,8 +497,8 @@ recv_challenge_reply(Socket, ChallengeA, Cookie) ->
true ->
?shutdown(bad_challenge_reply)
end;
- _ ->
- ?shutdown(no_node)
+ Other ->
+ ?shutdown({recv_challenge_reply,Other})
end.
send_challenge_ack(Socket, Digest) ->
@@ -461,37 +517,53 @@ send_challenge_ack(Socket, Digest) ->
% ?shutdown(bad_challenge_ack)
% end.
-send_name(Socket, MyNode0, Version) ->
- send_name(Socket, MyNode0, Version, ?COMPULSORY_DFLAGS).
-send_name(Socket, MyNode0, Version, Flags) ->
- MyNode = atom_to_list(MyNode0),
- ?to_port(Socket, [$n,?int16(Version),?int32(Flags)] ++
- MyNode).
+send_name(Socket, MyNode, OurVer, AssumedVer) ->
+ Flags = ?COMPULSORY_DFLAGS bor (case OurVer of
+ 5 -> 0;
+ 6 -> ?DFLAG_HANDSHAKE_23
+ end),
+ send_name(Socket, MyNode, OurVer, AssumedVer, Flags).
+
+send_name(Socket, MyNode, OurVer, AssumedVer, Flags) ->
+ NodeName = atom_to_binary(MyNode, latin1),
+ if AssumedVer =:= 5 ->
+ ?to_port(Socket, [$n,?int16(OurVer),?int32(Flags),NodeName]);
+ AssumedVer >= 6 ->
+ Creation = erts_internal:get_creation(),
+ ?to_port(Socket, [$N,
+ <<Flags:64,
+ Creation:32,
+ (byte_size(NodeName)):16>>,
+ NodeName])
+ end.
-%%
-%% recv_name is common for both old and new handshake.
-%%
-recv_name(Socket) ->
+recv_name(Socket, OurEpmdVer) ->
case gen_tcp:recv(Socket, 0) of
- {ok,Data} ->
- get_name(Data);
+ {ok,[$n, V1,V0, F3,F2,F1,F0 | OtherNode]} ->
+ 5 = OurEpmdVer,
+ 5 = ?u16(V1,V0),
+ Type = flags_to_type(?u32(F3,F2,F1,F0)),
+ {Type, list_to_atom(OtherNode)};
+ {ok,[$N, F7,F6,F5,F4,F3,F2,F1,F0, _Cr3,_Cr2,_Cr1,_Cr0, NL1, NL0 | Rest]} ->
+ true = (OurEpmdVer >= 6),
+ {OtherNode, _Residue} = lists:split(?u16(NL1,NL0), Rest),
+ <<Flags:64>> = <<F7,F6,F5,F4,F3,F2,F1,F0>>,
+ Type = flags_to_type(Flags),
+ {Type, list_to_atom(OtherNode)};
Res ->
?shutdown({no_node,Res})
end.
-get_name([$m,VersionA,VersionB,_Ip1,_Ip2,_Ip3,_Ip4|OtherNode]) ->
- {normal, list_to_atom(OtherNode), ?u16(VersionA,VersionB)};
-get_name([$h,VersionA,VersionB,_Ip1,_Ip2,_Ip3,_Ip4|OtherNode]) ->
- {hidden, list_to_atom(OtherNode), ?u16(VersionA,VersionB)};
-get_name([$n,VersionA, VersionB, Flag1, Flag2, Flag3, Flag4 | OtherNode]) ->
- Type = case ?u32(Flag1, Flag2, Flag3, Flag4) band ?DFLAG_PUBLISHED of
- 0 -> hidden;
- _ -> normal
- end,
- {Type, list_to_atom(OtherNode),
- ?u16(VersionA,VersionB)};
-get_name(Data) ->
- ?shutdown(Data).
+recv_complement(Socket, OurVer, 5) when OurVer > 5 ->
+ case gen_tcp:recv(Socket, 0) of
+ {ok,[$c, _F7,_F6,_F5,_F4, _Cr3,_Cr2,_Cr1,_Cr0]} ->
+ ok;
+ Res ->
+ ?shutdown({no_node,Res})
+ end;
+recv_complement(_, _OurVer, _OurEpmdVer) ->
+ ok.
+
%%
%% tell_name is for old handshake
@@ -536,13 +608,10 @@ wait_for_reg_reply(Socket, SoFar) ->
receive
{tcp, Socket, Data0} ->
case SoFar ++ Data0 of
- [$y, Result, A, B] ->
- case Result of
- 0 ->
- {alive, Socket, ?u16(A, B)};
- _ ->
- {error, duplicate_name}
- end;
+ [$y, 0, Cr1,Cr0] ->
+ {alive, Socket, ?u16(Cr1,Cr0)};
+ [$v, 0, Cr3,Cr2,Cr1,Cr0] ->
+ {alive, Socket, ?u32(Cr3,Cr2,Cr1,Cr0)};
Data when length(Data) < 4 ->
wait_for_reg_reply(Socket, Data);
Garbage ->
@@ -556,9 +625,9 @@ wait_for_reg_reply(Socket, SoFar) ->
end.
-register(NodeName, ListenSocket, VLow, VHigh) ->
+epmd_register(NodeName, ListenSocket, OurVer) ->
{ok,{_,TcpPort}} = inet:sockname(ListenSocket),
- case do_register_node(NodeName, TcpPort, VLow, VHigh) of
+ case do_register_node(NodeName, TcpPort, ?EI_DIST_LOW, OurVer) of
{alive, Socket, _Creation} ->
Socket;
Other ->