diff options
-rw-r--r-- | erts/doc/src/erl.xml | 4 | ||||
-rw-r--r-- | erts/doc/src/erl_dist_protocol.xml | 20 | ||||
-rw-r--r-- | erts/doc/src/erl_nif.xml | 55 | ||||
-rw-r--r-- | erts/emulator/drivers/common/inet_drv.c | 247 | ||||
-rw-r--r-- | lib/kernel/src/inet.erl | 67 | ||||
-rw-r--r-- | lib/kernel/src/local_tcp.erl | 2 | ||||
-rw-r--r-- | lib/kernel/test/gen_tcp_api_SUITE.erl | 265 | ||||
-rw-r--r-- | lib/kernel/test/gen_udp_SUITE.erl | 175 | ||||
-rw-r--r-- | lib/kernel/test/init_SUITE.erl | 2 |
9 files changed, 644 insertions, 193 deletions
diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index 436c2c57e1..37387f2c59 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -963,9 +963,7 @@ The amount of dirty IO schedulers is not limited by the amount of normal schedulers <seealso marker="#+SDcpu">like the amount of dirty CPU schedulers</seealso>. This since only I/O bound work is - expected to execute on dirty I/O schedulers. I/O bound jobs are - expected to either block waiting for I/O, and/or spend a limited - amount of time moving data. However, if the user should schedule CPU + expected to execute on dirty I/O schedulers. If the user should schedule CPU bound jobs on dirty I/O schedulers, these jobs might starve ordinary jobs executing on ordinary schedulers. </p> diff --git a/erts/doc/src/erl_dist_protocol.xml b/erts/doc/src/erl_dist_protocol.xml index f9fa981d9a..25f601a235 100644 --- a/erts/doc/src/erl_dist_protocol.xml +++ b/erts/doc/src/erl_dist_protocol.xml @@ -930,11 +930,14 @@ DiB == gen_digest(ChA,ICA) ? <tag><c>SEND</c></tag> <item> <p> - <c>{2, Cookie, ToPid}</c> + <c>{2, Unused, ToPid}</c> </p> <p> <em>Note</em> followed by <c>Message</c> </p> + <p> + <c>Unused</c> is kept for backward compatibility + </p> </item> <tag><c>EXIT</c></tag> @@ -961,11 +964,14 @@ DiB == gen_digest(ChA,ICA) ? <tag><c>REG_SEND</c></tag> <item> <p> - <c>{6, FromPid, Cookie, ToName}</c> + <c>{6, FromPid, Unused, ToName}</c> </p> <p> <em>Note</em> followed by <c>Message</c> </p> + <p> + <c>Unused</c> is kept for backward compatibility + </p> </item> <tag><c>GROUP_LEADER</c></tag> @@ -991,11 +997,14 @@ DiB == gen_digest(ChA,ICA) ? <tag><c>SEND_TT</c></tag> <item> <p> - <c>{12, Cookie, ToPid, TraceToken}</c> + <c>{12, Unused, ToPid, TraceToken}</c> </p> <p> <em>Note</em> followed by <c>Message</c> </p> + <p> + <c>Unused</c> is kept for backward compatibility + </p> </item> <tag><c>EXIT_TT</c></tag> @@ -1008,11 +1017,14 @@ DiB == gen_digest(ChA,ICA) ? <tag><c>REG_SEND_TT</c></tag> <item> <p> - <c>{16, FromPid, Cookie, ToName, TraceToken}</c> + <c>{16, FromPid, Unused, ToName, TraceToken}</c> </p> <p> <em>Note</em> followed by <c>Message</c> </p> + <p> + <c>Unused</c> is kept for backward compatibility + </p> </item> <tag><c>EXIT2_TT</c></tag> diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index c9b2092c17..f3921f1922 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -385,18 +385,39 @@ ok <p> A NIF that cannot be split and cannot execute in a millisecond or less is called a "dirty NIF" because it performs work that the - Erlang runtime cannot handle cleanly. Applications that make use - of such functions must indicate to the runtime that the functions - are dirty so they can be handled specially. To schedule a dirty - NIF for execution, the appropriate flags value can be set for the - NIF in its <seealso marker="#ErlNifFunc"><c>ErlNifFunc</c></seealso> + ordinary schedulers of the Erlang runtime system cannot handle cleanly. + Applications that make use of such functions must indicate to the + runtime that the functions are dirty so they can be handled + specially. This is handled by executing dirty jobs on a separate + set of schedulers called dirty schedulers. A dirty NIF executing + on a dirty scheduler does not have the same duration restriction + as a normal NIF. + </p> + + <p> + It is important to classify the dirty job correct. An I/O bound + job should be classified as such, and a CPU bound job should be + classified as such. If you should classify CPU bound jobs + as I/O bound jobs, dirty I/O schedulers might starve ordinary + schedulers. I/O bound jobs are expected to either block waiting + for I/O, and/or spend a limited amount of time moving data. + </p> + + <p> + To schedule a dirty NIF for execution, the appropriate + <c>flags</c> value can be set for the NIF in its + <seealso marker="#ErlNifFunc"><c>ErlNifFunc</c></seealso> entry, or the application can call <seealso marker="#enif_schedule_nif"><c>enif_schedule_nif</c></seealso>, passing to it a pointer to the dirty NIF to be executed and indicating with the <c>flags</c> argument whether it expects the - operation to be CPU-bound or I/O-bound. A dirty NIF executing - on a dirty scheduler does not have the same duration restriction - as a normal NIF. + operation to be CPU-bound or I/O-bound. A job that alternates + between I/O bound and CPU bound can be reclassified and + rescheduled using <c>enif_schedule_nif</c> so that it executes on + the correct type of dirty scheduler at all times. For more + information see the documentation of the <c>erl</c> command line + arguments <seealso marker="erl#+SDcpu"><c>+SDcpu</c></seealso>, + and <seealso marker="erl#+SDio"><c>+SDio</c></seealso>. </p> <p> @@ -442,24 +463,6 @@ ok dirty NIF has completed. </p> - <p> - It is important to classify the dirty job correct. An I/O bound - job should be classified as such and execute on a dirty I/O - scheduler, and a CPU bound job should be classified as such and - execute on a dirty CPU scheduler. If you classify CPU bound jobs - as I/O bound jobs, dirty I/O schedulers might starve ordinary - schedulers. For more information see the documentation - of the <c>erl</c> command line arguments - <seealso marker="erl#+SDcpu"><c>+SDcpu</c></seealso>, and - <seealso marker="erl#+SDio"><c>+SDio</c></seealso> as well - as <seealso marker="#enif_schedule_nif"><c>enif_schedule_nif</c></seealso>, - and <seealso marker="#ErlNifFunc"><c>ErlNifFunc</c></seealso>. - A job that alternates between I/O bound and CPU bound can - be reclassified and rescheduled using <c>enif_schedule_nif</c> - so that it exectutes on the correct type of dirty scheduler - at all times. - </p> - <p>Currently known issues that are planned to be fixed:</p> <list> <item> diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 93ea9f5dcb..5ce0e1de9e 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -591,6 +591,8 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) (((unsigned char*) (s))[1] << 8) | \ (((unsigned char*) (s))[0])) +#ifdef HAVE_SYS_UN_H + /* strnlen doesn't exist everywhere */ static size_t my_strnlen(const char *s, size_t maxlen) { @@ -608,6 +610,8 @@ static int is_nonzero(const char *s, size_t n) return 0; } +#endif + #ifdef VALGRIND # include <valgrind/memcheck.h> #else @@ -1301,6 +1305,8 @@ static ErlDrvTermData am_ssl_tls; static ErlDrvTermData am_udp; static ErlDrvTermData am_udp_passive; static ErlDrvTermData am_udp_error; +#endif +#ifdef HAVE_SYS_UN_H static ErlDrvTermData am_local; #endif #ifdef HAVE_SCTP @@ -1328,7 +1334,10 @@ static ErlDrvTermData am_ipv6_v6only; static ErlDrvTermData am_netns; #endif -/* speical errors for bad ports and sequences */ +static char str_eafnosupport[] = "eafnosupport"; +static char str_einval[] = "einval"; + +/* special errors for bad ports and sequences */ #define EXBADPORT "exbadport" #define EXBADSEQ "exbadseq" @@ -3846,6 +3855,8 @@ static int inet_init() #ifdef HAVE_UDP INIT_ATOM(udp_passive); INIT_ATOM(udp_error); +#endif +#ifdef HAVE_SYS_UN_H INIT_ATOM(local); #endif INIT_ATOM(empty_out_q); @@ -3955,74 +3966,88 @@ static int inet_init() /* ** Set an inaddr structure: -** src = [P1,P0,X1,X2,.....] +** *src = [P1,P0,X1,X2,.....] ** dst points to a structure large enugh to keep any kind ** of inaddr. ** *len is set to length of src on call ** and is set to actual length of dst on return -** return NULL on error and ptr after port address on success +** return NULL if ok or ptr to errno string for error */ static char* inet_set_address(int family, inet_address* dst, - char* src, ErlDrvSizeT* len) + char* *src, ErlDrvSizeT* len) { short port; - if ((family == AF_INET) && (*len >= 2+4)) { + switch (family) { + case AF_INET: { + if (*len < 2+4) return str_einval; sys_memzero((char*)dst, sizeof(struct sockaddr_in)); - port = get_int16(src); + port = get_int16(*src); #ifndef NO_SA_LEN dst->sai.sin_len = sizeof(struct sockaddr_in); #endif dst->sai.sin_family = family; dst->sai.sin_port = sock_htons(port); - sys_memcpy(&dst->sai.sin_addr, src+2, 4); + sys_memcpy(&dst->sai.sin_addr, (*src)+2, 4); *len = sizeof(struct sockaddr_in); - return src + 2+4; + *src += 2 + 4; + return NULL; } #if defined(HAVE_IN6) && defined(AF_INET6) - else if ((family == AF_INET6) && (*len >= 2+16)) { + case AF_INET6: { + if (*len < 2+16) return str_einval; sys_memzero((char*)dst, sizeof(struct sockaddr_in6)); - port = get_int16(src); + port = get_int16(*src); #ifndef NO_SA_LEN dst->sai6.sin6_len = sizeof(struct sockaddr_in6); #endif dst->sai6.sin6_family = family; dst->sai6.sin6_port = sock_htons(port); dst->sai6.sin6_flowinfo = 0; /* XXX this may be set as well ?? */ - sys_memcpy(&dst->sai6.sin6_addr, src+2, 16); - *len = sizeof(struct sockaddr_in6); - return src + 2+16; + sys_memcpy(&dst->sai6.sin6_addr, (*src)+2, 16); + *len = sizeof(struct sockaddr_in6); + *src += 2 + 16; + return NULL; } #endif #ifdef HAVE_SYS_UN_H - else if ((family == AF_UNIX) && (*len >= 1)) { - int n = *((unsigned char*)src); - if ((*len < 1+n) || (sizeof(dst->sal.sun_path) < n+1)) - return NULL; + case AF_UNIX: { + int n; + if (*len == 0) return str_einval; + n = *((unsigned char*)(*src)); /* Length field */ + if ((*len < 1+n) || (sizeof(dst->sal.sun_path) < n+1)) { + return str_einval; + } sys_memzero((char*)dst, sizeof(struct sockaddr_un)); dst->sal.sun_family = family; - sys_memcpy(dst->sal.sun_path, src+1, n); + sys_memcpy(dst->sal.sun_path, (*src)+1, n); *len = offsetof(struct sockaddr_un, sun_path) + n; - return src + 1 + n; + *src += 1 + n; + return NULL; } #endif - return NULL; + } + return str_eafnosupport; } /* ** Set an inaddr structure, address family comes from source data, ** or from argument if source data specifies constant address. ** -** src = [TAG,P1,P0] when TAG = INET_AF_ANY | INET_AF_LOOPBACK -** src = [TAG,P1,P0,X1,X2,...] when TAG = INET_AF_INET | INET_AF_INET6 | INET_AF_LOCAL +** *src = [TAG,P1,P0] +** when TAG = INET_AF_ANY | INET_AF_LOOPBACK +** *src = [TAG,P1,P0,X1,X2,...] +** when TAG = INET_AF_INET | INET_AF_INET6 | INET_AF_LOCAL +** *src = [TAG,Len,...] +** when TAG = INET_AF_LOCAL */ static char *inet_set_faddress(int family, inet_address* dst, - char *src, ErlDrvSizeT* len) { + char* *src, ErlDrvSizeT* len) { int tag; - if (*len < 1) return NULL; + if (*len < 1) return str_einval; (*len) --; - tag = *(src ++); + tag = *((*src) ++); switch (tag) { case INET_AF_INET: family = AF_INET; @@ -4042,8 +4067,8 @@ static char *inet_set_faddress(int family, inet_address* dst, case INET_AF_LOOPBACK: { int port; - if (*len < 2) return NULL; - port = get_int16(src); + if (*len < 2) return str_einval; + port = get_int16(*src); switch (family) { case AF_INET: { struct in_addr addr; @@ -4055,7 +4080,7 @@ static char *inet_set_faddress(int family, inet_address* dst, addr.s_addr = sock_htonl(INADDR_LOOPBACK); break; default: - return NULL; + return str_einval; } sys_memzero((char*)dst, sizeof(struct sockaddr_in)); #ifndef NO_SA_LEN @@ -4077,7 +4102,7 @@ static char *inet_set_faddress(int family, inet_address* dst, paddr = &in6addr_loopback; break; default: - return NULL; + return str_einval; } sys_memzero((char*)dst, sizeof(struct sockaddr_in6)); #ifndef NO_SA_LEN @@ -4091,12 +4116,13 @@ static char *inet_set_faddress(int family, inet_address* dst, } break; # endif default: - return NULL; + return str_einval; } - return src + 2; + *src += 2; + return NULL; } break; default: - return NULL; + return str_eafnosupport; } return inet_set_address(family, dst, src, len); } @@ -6691,7 +6717,6 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len) case SCTP_OPT_SET_PEER_PRIMARY_ADDR: { ErlDrvSizeT alen; - char *after; CHKLEN(curr, ASSOC_ID_LEN); /* XXX: These 2 opts have isomorphic value data structures, @@ -6702,12 +6727,9 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len) /* Fill in "arg.prim.sspp_addr": */ alen = ptr + len - curr; - after = inet_set_faddress(desc->sfamily, - (inet_address*) (&arg.prim.sspp_addr), - curr, &alen); - if (after == NULL) - return -1; - curr = after; + if (inet_set_faddress + (desc->sfamily, (inet_address*) (&arg.prim.sspp_addr), + &curr, &alen) != NULL) return -1; proto = IPPROTO_SCTP; if (eopt == SCTP_OPT_PRIMARY_ADDR) @@ -6733,7 +6755,6 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len) case SCTP_OPT_PEER_ADDR_PARAMS: { ErlDrvSizeT alen; - char *after; # ifdef HAVE_STRUCT_SCTP_PADDRPARAMS_SPP_FLAGS int eflags, cflags, hb_enable, hb_disable, pmtud_enable, pmtud_disable; @@ -6748,12 +6769,9 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len) /* Fill in "pap.spp_address": */ alen = ptr + len - curr; - after = inet_set_faddress(desc->sfamily, - (inet_address*) (&arg.pap.spp_address), - curr, &alen); - if (after == NULL) - return -1; - curr = after; + if (inet_set_faddress + (desc->sfamily, (inet_address*) (&arg.pap.spp_address), + &curr, &alen) != NULL) return -1; CHKLEN(curr, 4 + 2 + 3*4); @@ -7740,7 +7758,7 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, struct sctp_paddrparams ap; unsigned int sz = sizeof(ap); int n; - char *after; + char *before, *xerror; ErlDrvSizeT alen; if (buflen < ASSOC_ID_LEN) RETURN_ERROR(spec, -EINVAL); @@ -7748,13 +7766,22 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, buf += ASSOC_ID_LEN; buflen -= ASSOC_ID_LEN; alen = buflen; - after = inet_set_faddress(desc->sfamily, - (inet_address*) (&ap.spp_address), - buf, &alen); - if (after == NULL) RETURN_ERROR(spec, -EINVAL); - buflen -= after - buf; - buf = after; - + before = buf; + xerror = + inet_set_faddress + (desc->sfamily, (inet_address*) (&ap.spp_address), + &buf, &alen); + if (xerror != NULL) { +#ifdef EAFNOSUPPORT + if (xerror == str_eafnosupport) { + RETURN_ERROR(spec, -EAFNOSUPPORT); + } +#else + RETURN_ERROR(spec, -EINVAL); +#endif + } + buflen -= buf - before; + if (sock_getopt(desc->s, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &ap, &sz) < 0) continue; /* Fill in the response: */ @@ -7980,7 +8007,7 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, { struct sctp_paddrinfo pai; unsigned int sz = sizeof(pai); - char *after; + char *before, *xerror; ErlDrvSizeT alen; if (buflen < ASSOC_ID_LEN) RETURN_ERROR(spec, -EINVAL); @@ -7988,13 +8015,22 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, buf += ASSOC_ID_LEN; buflen -= ASSOC_ID_LEN; alen = buflen; - after = inet_set_faddress(desc->sfamily, - (inet_address*) (&pai.spinfo_address), - buf, &alen); - if (after == NULL) RETURN_ERROR(spec, -EINVAL); - buflen -= after - buf; - buf = after; - + before = buf; + xerror = + inet_set_faddress + (desc->sfamily, (inet_address*) (&pai.spinfo_address), + &buf, &alen); + if (xerror != NULL) { +#ifdef EAFNOSUPPORT + if (xerror == str_eafnosupport) { + RETURN_ERROR(spec, -EAFNOSUPPORT); + } +#else + RETURN_ERROR(spec, -EINVAL); +#endif + } + buflen -= buf - before; + if (sock_getopt(desc->s, IPPROTO_SCTP, SCTP_GET_PEER_ADDR_INFO, &pai, &sz) < 0) continue; /* Fill in the response: */ @@ -8511,15 +8547,16 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, } case INET_REQ_SETPEER: { /* set fake peername Port Address */ + char *xerror; if (len == 0) { desc->peer_ptr = NULL; return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } else if (len < 2) return ctl_error(EINVAL, rbuf, rsize); - else if (inet_set_faddress - (desc->sfamily, &desc->peer_addr, buf, &len) == NULL) - return ctl_error(EINVAL, rbuf, rsize); + else if ((xerror = inet_set_faddress + (desc->sfamily, &desc->peer_addr, &buf, &len)) != NULL) + return ctl_xerror(xerror, rbuf, rsize); else { desc->peer_ptr = &desc->peer_addr; desc->peer_addr_len = (SOCKLEN_T) len; @@ -8585,15 +8622,16 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, } case INET_REQ_SETNAME: { /* set fake sockname Port Address */ + char *xerror; if (len == 0) { desc->name_ptr = NULL; return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } else if (len < 2) return ctl_error(EINVAL, rbuf, rsize); - else if (inet_set_faddress - (desc->sfamily, &desc->name_addr, buf, &len) == NULL) - return ctl_error(EINVAL, rbuf, rsize); + else if ((xerror = inet_set_faddress + (desc->sfamily, &desc->name_addr, &buf, &len)) != NULL) + return ctl_xerror(xerror, rbuf, rsize); else { desc->name_ptr = &desc->name_addr; desc->name_addr_len = (SOCKLEN_T) len; @@ -8602,7 +8640,7 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, } case INET_REQ_BIND: { /* bind socket */ - char tbuf[2]; + char tbuf[2], *xerror; inet_address local; int port; @@ -8613,8 +8651,9 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, if (desc->state != INET_STATE_OPEN) return ctl_xerror(EXBADPORT, rbuf, rsize); - if (inet_set_faddress(desc->sfamily, &local, buf, &len) == NULL) - return ctl_error(EINVAL, rbuf, rsize); + if ((xerror = inet_set_faddress + (desc->sfamily, &local, &buf, &len)) != NULL) + return ctl_xerror(xerror, rbuf, rsize); if (IS_SOCKET_ERROR(sock_bind(desc->s,(struct sockaddr*) &local, len))) return ctl_error(sock_errno(), rbuf, rsize); @@ -9081,10 +9120,6 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, case INET_AF_INET6: domain = AF_INET6; break; -#else - case INET_AF_INET6: - return ctl_xerror("eafnosupport", rbuf, rsize); - break; #endif #ifdef HAVE_SYS_UN_H case INET_AF_LOCAL: @@ -9092,7 +9127,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, break; #endif default: - return ctl_error(EINVAL, rbuf, rsize); + return ctl_xerror(str_eafnosupport, rbuf, rsize); } if (buf[1] != INET_TYPE_STREAM) return ctl_error(EINVAL, rbuf, rsize); return inet_ctl_open(INETP(desc), domain, SOCK_STREAM, rbuf, rsize); @@ -9112,10 +9147,6 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, case INET_AF_INET6: domain = AF_INET6; break; -#else - case INET_AF_INET6: - return ctl_xerror("eafnosupport", rbuf, rsize); - break; #endif #ifdef HAVE_SYS_UN_H case INET_AF_LOCAL: @@ -9123,7 +9154,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, break; #endif default: - return ctl_error(EINVAL, rbuf, rsize); + return ctl_xerror(str_eafnosupport, rbuf, rsize); } if (buf[1] != INET_TYPE_STREAM) return ctl_error(EINVAL, rbuf, rsize); @@ -9156,7 +9187,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, case INET_REQ_CONNECT: { /* do async connect */ int code; - char tbuf[2]; + char tbuf[2], *xerror; unsigned timeout; DEBUGF(("tcp_inet_ctl(%ld): CONNECT\r\n", (long)desc->inet.port)); @@ -9173,9 +9204,9 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, timeout = get_int32(buf); buf += 4; len -= 4; - if (inet_set_faddress - (desc->inet.sfamily, &desc->inet.remote, buf, &len) == NULL) - return ctl_error(EINVAL, rbuf, rsize); + if ((xerror = inet_set_faddress + (desc->inet.sfamily, &desc->inet.remote, &buf, &len)) != NULL) + return ctl_xerror(xerror, rbuf, rsize); code = sock_connect(desc->inet.s, (struct sockaddr*) &desc->inet.remote, len); @@ -10955,16 +10986,12 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, case INET_AF_INET: af = AF_INET; break; #if defined(HAVE_IN6) && defined(AF_INET6) case INET_AF_INET6: af = AF_INET6; break; -#else - case INET_AF_INET6: - return ctl_xerror("eafnosupport", rbuf, rsize); - break; #endif #ifdef HAVE_SYS_UN_H case INET_AF_LOCAL: af = AF_UNIX; break; #endif default: - return ctl_error(EINVAL, rbuf, rsize); + return ctl_xerror(str_eafnosupport, rbuf, rsize); } switch (buf[1]) { case INET_TYPE_STREAM: type = SOCK_STREAM; break; @@ -11008,16 +11035,12 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, case INET_AF_INET: af = AF_INET; break; #if defined(HAVE_IN6) && defined(AF_INET6) case INET_AF_INET6: af = AF_INET6; break; -#else - case INET_AF_INET6: - return ctl_xerror("eafnosupport", rbuf, rsize); - break; #endif #ifdef HAVE_SYS_UN_H case INET_AF_LOCAL: af = AF_UNIX; break; #endif default: - return ctl_error(EINVAL, rbuf, rsize); + return ctl_xerror(str_eafnosupport, rbuf, rsize); } switch (buf[1]) { case INET_TYPE_STREAM: type = SOCK_STREAM; break; @@ -11085,6 +11108,7 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, #ifdef HAVE_SCTP if (IS_SCTP(desc)) { inet_address remote; + char *xerror; if (IS_CONNECTING(desc)) return ctl_error(EINVAL, rbuf, rsize); @@ -11096,8 +11120,9 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, /* For SCTP, we do not set the peer's addr in desc->remote, as multiple peers are possible: */ - if (inet_set_faddress(desc->sfamily, &remote, buf, &len) == NULL) - return ctl_error(EINVAL, rbuf, rsize); + if ((xerror = inet_set_faddress + (desc->sfamily, &remote, &buf, &len)) != NULL) + return ctl_xerror(xerror, rbuf, rsize); sock_select(desc, FD_CONNECT, 1); code = sock_connect(desc->s, &remote.sa, len); @@ -11133,12 +11158,13 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, else if (len < 6) return ctl_error(EINVAL, rbuf, rsize); else { + char *xerror; /* Ignore timeout */ buf += 4; len -= 4; - if (inet_set_faddress - (desc->sfamily, &desc->remote, buf, &len) == NULL) - return ctl_error(EINVAL, rbuf, rsize); + if ((xerror = inet_set_faddress + (desc->sfamily, &desc->remote, &buf, &len)) != NULL) + return ctl_xerror(xerror, rbuf, rsize); code = sock_connect(desc->s, (struct sockaddr*) &desc->remote, len); @@ -11202,11 +11228,13 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, while (curr < buf+len) { + char *xerror; /* List item format: see "inet_set_faddress": */ ErlDrvSizeT alen = buf + len - curr; - curr = inet_set_faddress(desc->sfamily, &addr, curr, &alen); - if (curr == NULL) - return ctl_error(EINVAL, rbuf, rsize); + xerror = inet_set_faddress + (desc->sfamily, &addr, &curr, &alen); + if (xerror != NULL) + return ctl_xerror(xerror, rbuf, rsize); /* Invoke the call: */ if (p_sctp_bindx(desc->s, (struct sockaddr *)&addr, 1, @@ -11325,6 +11353,7 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len) inet_descriptor* desc = INETP(udesc); char* ptr = buf; char* qtr; + char* xerror; ErlDrvSizeT sz; int code; inet_address other; @@ -11387,9 +11416,10 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len) /* UDP socket. Even if it is connected, there is an address prefix here -- ignored for connected sockets: */ sz = len; - qtr = inet_set_faddress(desc->sfamily, &other, ptr, &sz); - if (qtr == NULL) { - inet_reply_error(desc, EINVAL); + qtr = ptr; + xerror = inet_set_faddress(desc->sfamily, &other, &qtr, &sz); + if (xerror != NULL) { + inet_reply_error_am(desc, driver_mk_atom(xerror)); return; } len -= (qtr - ptr); @@ -11975,7 +12005,7 @@ void erts_sock_close(erts_sock_t socket) int erts_sock_connect(erts_sock_t socket, byte *ip_addr, int len, Uint16 port) { SOCKET s = (SOCKET) socket; - char buf[2 + 4]; + char buf[2 + 4], *p; ErlDrvSizeT blen = 6; inet_address addr; @@ -11985,7 +12015,8 @@ int erts_sock_connect(erts_sock_t socket, byte *ip_addr, int len, Uint16 port) put_int16(port, buf); memcpy((void *) (buf + 2), (void *) ip_addr, 4); - if (!inet_set_address(AF_INET, &addr, buf, &blen)) + p = buf; + if (inet_set_address(AF_INET, &addr, &p, &blen) != NULL) return 0; if (IS_SOCKET_ERROR diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 9fc685e728..a91a6ed517 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -1344,18 +1344,12 @@ open(FdO, Addr, Port, Opts, Protocol, Family, Type, Module) case prim_inet:open(Protocol, Family, Type, OpenOpts) of {ok,S} -> case prim_inet:setopts(S, Opts) of + ok when Addr =:= undefined -> + inet_db:register_socket(S, Module), + {ok,S}; ok -> - case - case Addr of - undefined -> - {ok, undefined}; - _ when is_list(Addr) -> - bindx(S, Addr, Port); - _ -> - prim_inet:bind(S, Addr, Port) - end - of - {ok, _} -> + case bind(S, Addr, Port) of + {ok, _} -> inet_db:register_socket(S, Module), {ok,S}; Error -> @@ -1373,6 +1367,11 @@ open(Fd, Addr, Port, Opts, Protocol, Family, Type, Module) when is_integer(Fd) -> fdopen(Fd, Addr, Port, Opts, Protocol, Family, Type, Module). +bind(S, Addr, Port) when is_list(Addr) -> + bindx(S, Addr, Port); +bind(S, Addr, Port) -> + prim_inet:bind(S, Addr, Port). + bindx(S, [Addr], Port0) -> {IP, Port} = set_bindx_port(Addr, Port0), prim_inet:bind(S, IP, Port); @@ -1413,34 +1412,36 @@ fdopen(Fd, Opts, Protocol, Family, Type, Module) -> fdopen(Fd, any, 0, Opts, Protocol, Family, Type, Module). fdopen(Fd, Addr, Port, Opts, Protocol, Family, Type, Module) -> - IsAnyAddr = (Addr == {0,0,0,0} orelse Addr == {0,0,0,0,0,0,0,0} - orelse Addr == any), - Bound = Port == 0 andalso IsAnyAddr, + Bound = + %% We do not do any binding if default port+addr options + %% were given, in order to keep backwards compatability + %% with pre Erlang/OTP 17 + case Addr of + {0,0,0,0} when Port =:= 0 -> true; + {0,0,0,0,0,0,0,0} when Port =:= 0 -> true; + any when Port =:= 0 -> true; + _ -> false + end, case prim_inet:fdopen(Protocol, Family, Type, Fd, Bound) of {ok, S} -> case prim_inet:setopts(S, Opts) of + ok + when Addr =:= undefined; + Bound -> + inet_db:register_socket(S, Module), + {ok, S}; ok -> - case if - Bound -> - %% We do not do any binding if default - %% port+addr options where given in order - %% to keep backwards compatability with - %% pre Erlang/TOP 17 - {ok, ok}; - is_list(Addr) -> - bindx(S, Addr, Port); - true -> - prim_inet:bind(S, Addr, Port) - end of - {ok, _} -> - inet_db:register_socket(S, Module), - {ok, S}; - Error -> - prim_inet:close(S), - Error + case bind(S, Addr, Port) of + {ok, _} -> + inet_db:register_socket(S, Module), + {ok, S}; + Error -> + prim_inet:close(S), + Error end; Error -> - prim_inet:close(S), Error + prim_inet:close(S), + Error end; Error -> Error end. diff --git a/lib/kernel/src/local_tcp.erl b/lib/kernel/src/local_tcp.erl index e3c67dfbb7..90e0fa2162 100644 --- a/lib/kernel/src/local_tcp.erl +++ b/lib/kernel/src/local_tcp.erl @@ -175,4 +175,4 @@ accept(L, Timeout) -> %% Create a port/socket from a file descriptor %% fdopen(Fd, Opts) -> - inet:fdopen(Fd, Opts, ?PROTO, ?FAMILY, ?TYPE, ?MODULE). + inet:open(Fd, undefined, 0, Opts, ?PROTO, ?FAMILY, ?TYPE, ?MODULE). diff --git a/lib/kernel/test/gen_tcp_api_SUITE.erl b/lib/kernel/test/gen_tcp_api_SUITE.erl index 54298e6309..77ec89b4f4 100644 --- a/lib/kernel/test/gen_tcp_api_SUITE.erl +++ b/lib/kernel/test/gen_tcp_api_SUITE.erl @@ -34,7 +34,11 @@ t_recv_timeout/1, t_recv_eof/1, t_recv_delim/1, t_shutdown_write/1, t_shutdown_both/1, t_shutdown_error/1, t_shutdown_async/1, - t_fdopen/1, t_fdconnect/1, t_implicit_inet6/1]). + t_fdopen/1, t_fdconnect/1, t_implicit_inet6/1, + t_local_basic/1, t_local_unbound/1, t_local_fdopen/1, + t_local_fdopen_listen/1, t_local_fdopen_listen_unbound/1, + t_local_fdopen_connect/1, t_local_fdopen_connect_unbound/1, + t_local_abstract/1]). -export([getsockfd/0,closesockfd/1]). @@ -45,12 +49,18 @@ suite() -> all() -> [{group, t_accept}, {group, t_connect}, {group, t_recv}, t_shutdown_write, t_shutdown_both, t_shutdown_error, - t_shutdown_async, t_fdopen, t_fdconnect, t_implicit_inet6]. + t_shutdown_async, t_fdopen, t_fdconnect, t_implicit_inet6, + {group, t_local}]. groups() -> [{t_accept, [], [t_accept_timeout]}, {t_connect, [], [t_connect_timeout, t_connect_bad]}, - {t_recv, [], [t_recv_timeout, t_recv_eof, t_recv_delim]}]. + {t_recv, [], [t_recv_timeout, t_recv_eof, t_recv_delim]}, + {t_local, [], + [t_local_basic, t_local_unbound, t_local_fdopen, + t_local_fdopen_listen, t_local_fdopen_listen_unbound, + t_local_fdopen_connect, t_local_fdopen_connect_unbound, + t_local_abstract]}]. @@ -60,17 +70,36 @@ init_per_suite(Config) -> end_per_suite(_Config) -> ok. +init_per_group(t_local, Config) -> + case gen_tcp:connect({local,<<"/">>}, 0, []) of + {error,eafnosupport} -> + {skip, "AF_LOCAL not supported"}; + {error,_} -> + Config + end; init_per_group(_GroupName, Config) -> Config. -end_per_group(_,_Config) -> +end_per_group(t_local, _Config) -> + delete_local_filenames(); +end_per_group(_, _Config) -> ok. + +init_per_testcase(Func, Config) + when Func =:= undefined -> % Insert your testcase name here + dbg:tracer(), + dbg:p(self(), c), + dbg:tpl(prim_inet, cx), + dbg:tpl(local_tcp, cx), + dbg:tpl(inet, cx), + dbg:tpl(gen_tcp, cx), + Config; init_per_testcase(_Func, Config) -> Config. end_per_testcase(_Func, _Config) -> - ok. + dbg:stop(). %%% gen_tcp:accept/1,2 @@ -279,9 +308,7 @@ t_implicit_inet6(Host, Addr) -> implicit_inet6(S1, Loopback), ok = gen_tcp:close(S1), %% - Localhost = "localhost", - Localaddr = ok(inet:getaddr(Localhost, inet6)), - io:format("~s ~p~n", [Localhost,Localaddr]), + Localaddr = ok(get_localaddr()), S2 = ok(gen_tcp:listen(0, [{ip,Localaddr}])), implicit_inet6(S2, Localaddr), ok = gen_tcp:close(S2), @@ -308,6 +335,192 @@ implicit_inet6(S, Addr) -> ok = gen_tcp:close(S1). + +t_local_basic(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + CFile = local_filename(client), + CAddr = {local,bin_filename(CFile)}, + _ = file:delete(SFile), + _ = file:delete(CFile), + %% + L = + ok( + gen_tcp:listen(0, [{ifaddr,{local,SFile}},{active,false}])), + C = + ok( + gen_tcp:connect( + {local,SFile}, 0, [{ifaddr,{local,CFile}},{active,false}])), + S = ok(gen_tcp:accept(L)), + SAddr = ok(inet:sockname(L)), + {error,enotconn} = inet:peername(L), + local_handshake(S, SAddr, C, CAddr), + ok = gen_tcp:close(L), + ok = gen_tcp:close(S), + ok = gen_tcp:close(C), + %% + ok = file:delete(SFile), + ok = file:delete(CFile), + ok. + +t_local_unbound(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + _ = file:delete(SFile), + %% + L = ok(gen_tcp:listen(0, [{ifaddr,SAddr},{active,false}])), + C = ok(gen_tcp:connect(SAddr, 0, [{active,false}])), + S = ok(gen_tcp:accept(L)), + SAddr = ok(inet:sockname(L)), + {error,enotconn} = inet:peername(L), + local_handshake(S, SAddr, C, {local,<<>>}), + ok = gen_tcp:close(L), + ok = gen_tcp:close(S), + ok = gen_tcp:close(C), + ok = file:delete(SFile), + ok. + +t_local_fdopen(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + _ = file:delete(SFile), + %% + L = ok(gen_tcp:listen(0, [{ifaddr,SAddr},{active,false}])), + C0 = ok(gen_tcp:connect(SAddr, 0, [{active,false}])), + Fd = ok(prim_inet:getfd(C0)), + ok = prim_inet:ignorefd(C0, true), + C = ok(gen_tcp:fdopen(Fd, [local])), + S = ok(gen_tcp:accept(L)), + SAddr = ok(inet:sockname(L)), + {error,enotconn} = inet:peername(L), + local_handshake(S, SAddr, C, {local,<<>>}), + ok = gen_tcp:close(L), + ok = gen_tcp:close(S), + ok = gen_tcp:close(C), + ok = gen_tcp:close(C0), + ok = file:delete(SFile), + ok. + +t_local_fdopen_listen(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + _ = file:delete(SFile), + L0 = ok(gen_tcp:listen(0, [{ifaddr,SAddr},{active,false}])), + Fd = ok(prim_inet:getfd(L0)), + L = ok(gen_tcp:listen(0, [{fd,Fd},local,{active,false}])), + C = ok(gen_tcp:connect(SAddr, 0, [{active,false}])), + S = ok(gen_tcp:accept(L)), + SAddr = ok(inet:sockname(L)), + {error,enotconn} = inet:peername(L), + local_handshake(S, SAddr, C, {local,<<>>}), + ok = gen_tcp:close(L), + ok = gen_tcp:close(L0), + ok = gen_tcp:close(S), + ok = gen_tcp:close(C), + ok = file:delete(SFile), + ok. + +t_local_fdopen_listen_unbound(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + _ = file:delete(SFile), + P = ok(prim_inet:open(tcp, local, stream)), + Fd = ok(prim_inet:getfd(P)), + L = + ok(gen_tcp:listen( + 0, [{fd,Fd},{ifaddr,SAddr},{active,false}])), + C = ok(gen_tcp:connect(SAddr, 0, [{active,false}])), + S = ok(gen_tcp:accept(L)), + SAddr = ok(inet:sockname(L)), + {error,enotconn} = inet:peername(L), + local_handshake(S, SAddr, C, {local,<<>>}), + ok = gen_tcp:close(L), + ok = gen_tcp:close(P), + ok = gen_tcp:close(S), + ok = gen_tcp:close(C), + ok = file:delete(SFile), + ok. + +t_local_fdopen_connect(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + CFile = local_filename(client), + CAddr = {local,bin_filename(CFile)}, + _ = file:delete(SFile), + _ = file:delete(CFile), + L = ok(gen_tcp:listen(0, [{ifaddr,SAddr},{active,false}])), + P = ok(prim_inet:open(tcp, local, stream)), + Fd = ok(prim_inet:getfd(P)), + C = + ok(gen_tcp:connect( + SAddr, 0, [{fd,Fd},{ifaddr,CAddr},{active,false}])), + S = ok(gen_tcp:accept(L)), + SAddr = ok(inet:sockname(L)), + {error,enotconn} = inet:peername(L), + local_handshake(S, SAddr, C, CAddr), + ok = gen_tcp:close(L), + ok = gen_tcp:close(S), + ok = gen_tcp:close(C), + ok = gen_tcp:close(P), + ok = file:delete(SFile), + ok. + +t_local_fdopen_connect_unbound(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + _ = file:delete(SFile), + L = ok(gen_tcp:listen(0, [{ifaddr,SAddr},{active,false}])), + P = ok(prim_inet:open(tcp, local, stream)), + Fd = ok(prim_inet:getfd(P)), + C = ok(gen_tcp:connect(SAddr, 0, [{fd,Fd},{active,false}])), + S = ok(gen_tcp:accept(L)), + SAddr = ok(inet:sockname(L)), + {error,enotconn} = inet:peername(L), + local_handshake(S, SAddr, C, {local,<<>>}), + ok = gen_tcp:close(L), + ok = gen_tcp:close(S), + ok = gen_tcp:close(C), + ok = gen_tcp:close(P), + ok = file:delete(SFile), + ok. + +t_local_abstract(_Config) -> + case os:type() of + {unix,linux} -> + AbstAddr = {local,<<>>}, + L = + ok(gen_tcp:listen( + 0, [{ifaddr,AbstAddr},{active,false}])), + {local,_} = SAddr = ok(inet:sockname(L)), + C = + ok(gen_tcp:connect( + SAddr, 0, [{ifaddr,AbstAddr},{active,false}])), + {local,_} = CAddr = ok(inet:sockname(C)), + S = ok(gen_tcp:accept(L)), + {error,enotconn} = inet:peername(L), + local_handshake(S, SAddr, C, CAddr), + ok = gen_tcp:close(L), + ok = gen_tcp:close(S), + ok = gen_tcp:close(C), + ok; + _ -> + {skip,"AF_LOCAL Abstract Addresses only supported on Linux"} + end. + + +local_handshake(S, SAddr, C, CAddr) -> + SData = "9876543210", + CData = "0123456789", + SAddr = ok(inet:sockname(S)), + CAddr = ok(inet:sockname(C)), + CAddr = ok(inet:peername(S)), + SAddr = ok(inet:peername(C)), + ok = gen_tcp:send(C, CData), + ok = gen_tcp:send(S, SData), + CData = ok(gen_tcp:recv(S, length(CData))), + SData = ok(gen_tcp:recv(C, length(SData))), + ok. + %%% Utilities %% Calls M:F/length(A), which should return a timeout error, and complete @@ -369,8 +582,42 @@ unused_ip(A, B, C, D) -> {error, _} -> {ok, {A, B, C, D}} end. -ok({ok,V}) -> V. +ok({ok,V}) -> V; +ok(NotOk) -> + try throw(not_ok) + catch + Thrown -> + erlang:raise( + error, {Thrown, NotOk}, tl(erlang:get_stacktrace())) + end. +get_localaddr() -> + get_localaddr(["localhost", "localhost6", "ip6-localhost"]). + +get_localaddr([]) -> + {error, localaddr_not_found}; +get_localaddr([Localhost|Ls]) -> + case inet:getaddr(Localhost, inet6) of + {ok, LocalAddr} -> + io:format("~s ~p~n", [Localhost, LocalAddr]), + {ok, LocalAddr}; + _ -> + get_localaddr(Ls) + end. getsockfd() -> undefined. closesockfd(_FD) -> undefined. + +local_filename(Tag) -> + "/tmp/" ?MODULE_STRING "_" ++ os:getpid() ++ "_" ++ atom_to_list(Tag). + +bin_filename(String) -> + unicode:characters_to_binary(String, file:native_name_encoding()). + +delete_local_filenames() -> + _ = + [file:delete(F) || + F <- + filelib:wildcard( + "/tmp/" ?MODULE_STRING "_" ++ os:getpid() ++ "_*")], + ok. diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index 44539bf44c..1029d7ef0a 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -35,7 +35,9 @@ -export([send_to_closed/1, active_n/1, buffer_size/1, binary_passive_recv/1, bad_address/1, - read_packets/1, open_fd/1, connect/1, implicit_inet6/1]). + read_packets/1, open_fd/1, connect/1, implicit_inet6/1, + local_basic/1, local_unbound/1, + local_fdopen/1, local_fdopen_unbound/1, local_abstract/1]). suite() -> [{ct_hooks,[ts_install_cth]}, @@ -44,10 +46,13 @@ suite() -> all() -> [send_to_closed, buffer_size, binary_passive_recv, bad_address, read_packets, open_fd, connect, - implicit_inet6, active_n]. + implicit_inet6, active_n, + {group, local}]. groups() -> - []. + [{local, [], + [local_basic, local_unbound, + local_fdopen, local_fdopen_unbound, local_abstract]}]. init_per_suite(Config) -> Config. @@ -55,9 +60,19 @@ init_per_suite(Config) -> end_per_suite(_Config) -> ok. +init_per_group(local, Config) -> + case gen_udp:open(0, [local]) of + {ok,S} -> + ok = gen_udp:close(S), + Config; + {error,eafnosupport} -> + {skip, "AF_LOCAL not supported"} + end; init_per_group(_GroupName, Config) -> Config. +end_per_group(local, _Config) -> + delete_local_filenames(); end_per_group(_GroupName, Config) -> Config. @@ -65,7 +80,7 @@ end_per_group(_GroupName, Config) -> init_per_testcase(_Case, Config) -> Config. -end_per_testcase(_Case, Config) -> +end_per_testcase(_Case, _Config) -> ok. %%------------------------------------------------------------- @@ -550,6 +565,116 @@ active_n(Config) when is_list(Config) -> ok = gen_udp:close(S1), ok. + +local_basic(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + CFile = local_filename(client), + CAddr = {local,bin_filename(CFile)}, + _ = file:delete(SFile), + _ = file:delete(CFile), + %% + S = ok(gen_udp:open(0, [{ifaddr,{local,SFile}},{active,false}])), + C = ok(gen_udp:open(0, [{ifaddr,{local,CFile}},{active,false}])), + SAddr = ok(inet:sockname(S)), + CAddr = ok(inet:sockname(C)), + local_handshake(S, SAddr, C, CAddr), + ok = gen_udp:close(S), + ok = gen_udp:close(C), + %% + ok = file:delete(SFile), + ok = file:delete(CFile), + ok. + +local_unbound(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + _ = file:delete(SFile), + %% + S = ok(gen_udp:open(0, [{ifaddr,SAddr},{active,false}])), + C = ok(gen_udp:open(0, [local,{active,false}])), + SAddr = ok(inet:sockname(S)), + local_handshake(S, SAddr, C, undefined), + ok = gen_udp:close(S), + ok = gen_udp:close(C), + %% + ok = file:delete(SFile), + ok. + +local_fdopen(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + CFile = local_filename(client), + CAddr = {local,bin_filename(CFile)}, + _ = file:delete(SFile), + _ = file:delete(CFile), + %% + S0 = ok(gen_udp:open(0, [{ifaddr,SAddr},{active,false}])), + C = ok(gen_udp:open(0, [{ifaddr,{local,CFile}},{active,false}])), + SAddr = ok(inet:sockname(S0)), + CAddr = ok(inet:sockname(C)), + Fd = ok(prim_inet:getfd(S0)), + S = ok(gen_udp:open(0, [{fd,Fd},local,{active,false}])), + SAddr = ok(inet:sockname(S)), + local_handshake(S, SAddr, C, CAddr), + ok = gen_udp:close(S), + ok = gen_udp:close(S0), + ok = gen_udp:close(C), + %% + ok = file:delete(SFile), + ok = file:delete(CFile), + ok. + +local_fdopen_unbound(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + _ = file:delete(SFile), + %% + S = ok(gen_udp:open(0, [{ifaddr,SAddr},{active,false}])), + C0 = ok(gen_udp:open(0, [local,{active,false}])), + SAddr = ok(inet:sockname(S)), + Fd = ok(prim_inet:getfd(C0)), + C = ok(gen_udp:open(0, [{fd,Fd},local,{active,false}])), + local_handshake(S, SAddr, C, undefined), + ok = gen_udp:close(S), + ok = gen_udp:close(C), + ok = gen_udp:close(C0), + %% + ok = file:delete(SFile), + ok. + +local_abstract(_Config) -> + case os:type() of + {unix,linux} -> + S = ok(gen_udp:open(0, [{ifaddr,{local,<<>>}},{active,false}])), + C = ok(gen_udp:open(0, [{ifaddr,{local,<<>>}},{active,false}])), + {local,_} = SAddr = ok(inet:sockname(S)), + {local,_} = CAddr = ok(inet:sockname(C)), + local_handshake(S, SAddr, C, CAddr), + ok = gen_udp:close(S), + ok = gen_udp:close(C), + ok; + _ -> + {skip,"AF_LOCAL Abstract Addresses only supported on Linux"} + end. + + +local_handshake(S, SAddr, C, CAddr) -> + SData = "9876543210", + CData = "0123456789", + ok = gen_udp:send(C, SAddr, 0, CData), + case ok(gen_tcp:recv(S, 112)) of + {{unspec,<<>>}, 0, CData} when CAddr =:= undefined -> + ok; + {{local,<<>>}, 0, CData} when CAddr =:= undefined -> + ok; + {CAddr, 0, CData} when CAddr =/= undefined -> + ok = gen_udp:send(S, CAddr, 0, SData), + {SAddr, 0, SData} = ok(gen_tcp:recv(C, 112)), + ok + + end. + %% %% Utils %% @@ -599,9 +724,7 @@ implicit_inet6(Host, Addr) -> implicit_inet6(S1, Active, Loopback), ok = gen_udp:close(S1), %% - Localhost = "localhost", - Localaddr = ok(inet:getaddr(Localhost, inet6)), - io:format("~s ~p~n", [Localhost,Localaddr]), + Localaddr = ok(get_localaddr()), S2 = ok(gen_udp:open(0, [{ip,Localaddr},Active])), implicit_inet6(S2, Active, Localaddr), ok = gen_udp:close(S2), @@ -630,4 +753,40 @@ implicit_inet6(S1, Active, Addr) -> {Addr,P2,"pong"} = ok(gen_udp:recv(S1, 1024)), ok = gen_udp:close(S2). -ok({ok,V}) -> V. +ok({ok,V}) -> V; +ok(NotOk) -> + try throw(not_ok) + catch + Thrown -> + erlang:raise( + error, {Thrown, NotOk}, tl(erlang:get_stacktrace())) + end. + + +local_filename(Tag) -> + "/tmp/" ?MODULE_STRING "_" ++ os:getpid() ++ "_" ++ atom_to_list(Tag). + +bin_filename(String) -> + unicode:characters_to_binary(String, file:native_name_encoding()). + +delete_local_filenames() -> + _ = + [file:delete(F) || + F <- + filelib:wildcard( + "/tmp/" ?MODULE_STRING "_" ++ os:getpid() ++ "_*")], + ok. + +get_localaddr() -> + get_localaddr(["localhost", "localhost6", "ip6-localhost"]). + +get_localaddr([]) -> + {error, localaddr_not_found}; +get_localaddr([Localhost|Ls]) -> + case inet:getaddr(Localhost, inet6) of + {ok, LocalAddr} -> + io:format("~s ~p~n", [Localhost, LocalAddr]), + {ok, LocalAddr}; + _ -> + get_localaddr(Ls) + end. diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl index a332e7966b..1370e23195 100644 --- a/lib/kernel/test/init_SUITE.erl +++ b/lib/kernel/test/init_SUITE.erl @@ -298,7 +298,7 @@ many_restarts() -> many_restarts(Config) when is_list(Config) -> {ok, Node} = loose_node:start(init_test, "", ?DEFAULT_TIMEOUT_SEC), - loop_restart(30,Node,rpc:call(Node,erlang,whereis,[error_logger])), + loop_restart(50,Node,rpc:call(Node,erlang,whereis,[error_logger])), loose_node:stop(Node), ok. |