diff options
author | Sverker Eriksson <sverker@erlang.org> | 2020-01-14 15:21:38 +0100 |
---|---|---|
committer | Sverker Eriksson <sverker@erlang.org> | 2020-01-14 15:31:02 +0100 |
commit | cf6cf5e5f82e348ecb9bb02d70027fc4961aee3d (patch) | |
tree | ea7b4071477fde92e648bd2f3cbe80627214c7dd /lib/erl_interface | |
parent | 1df82a8b75d83f07f979f92a7906e30dac8ce620 (diff) | |
parent | fc309a039a4d817725d7b9887a36d2c501a83679 (diff) | |
download | erlang-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.h | 2 | ||||
-rw-r--r-- | lib/erl_interface/src/connect/ei_connect.c | 478 | ||||
-rw-r--r-- | lib/erl_interface/src/connect/ei_connect_int.h | 2 | ||||
-rw-r--r-- | lib/erl_interface/src/epmd/ei_epmd.h | 7 | ||||
-rw-r--r-- | lib/erl_interface/test/ei_accept_SUITE.erl | 3 | ||||
-rw-r--r-- | lib/erl_interface/test/ei_tmo_SUITE.erl | 201 |
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 -> |