diff options
Diffstat (limited to 'erts')
-rw-r--r-- | erts/configure.in | 2 | ||||
-rw-r--r-- | erts/emulator/beam/io.c | 2 | ||||
-rw-r--r-- | erts/emulator/drivers/common/inet_drv.c | 242 | ||||
-rw-r--r-- | erts/preloaded/ebin/prim_inet.beam | bin | 71452 -> 73252 bytes | |||
-rw-r--r-- | erts/preloaded/src/prim_inet.erl | 70 |
5 files changed, 296 insertions, 20 deletions
diff --git a/erts/configure.in b/erts/configure.in index 904544c85c..9d67837ff0 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -1658,7 +1658,7 @@ if test "x$enable_sctp" != "xno" ; then fi if test x"$ac_cv_header_netinet_sctp_h" = x"yes"; then - AC_CHECK_FUNCS([sctp_bindx sctp_peeloff]) + AC_CHECK_FUNCS([sctp_bindx sctp_peeloff sctp_getladdrs sctp_freeladdrs sctp_getpaddrs sctp_freepaddrs]) AC_CHECK_DECLS([SCTP_UNORDERED, SCTP_ADDR_OVER, SCTP_ABORT, SCTP_EOF, SCTP_SENDALL, SCTP_ADDR_CONFIRMED, SCTP_DELAYED_ACK_TIME, diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index b2803747eb..60c3ce2de5 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -4079,7 +4079,7 @@ erts_port_control(Process* c_p, copy = 1; else { binp = ((ProcBin *) ebinp)->val; - ASSERT(bufp < bufp + size); + ASSERT(bufp <= bufp + size); ASSERT(binp->orig_bytes <= bufp && bufp + size <= binp->orig_bytes + binp->orig_size); erts_refc_inc(&binp->refc, 1); diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 4fb20bf1b0..978a766de9 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -424,13 +424,44 @@ static unsigned long one_value = 1; # define sctp_adaptation_layer_event sctp_adaption_layer_event #endif -#ifdef __GNUC__ +#if defined(__GNUC__) && defined(HAVE_SCTP_BINDX) static typeof(sctp_bindx) *p_sctp_bindx = NULL; +#else +static int (*p_sctp_bindx) + (int sd, struct sockaddr *addrs, int addrcnt, int flags) = NULL; +#endif + +#if defined(__GNUC__) && defined(HAVE_SCTP_PEELOFF) static typeof(sctp_peeloff) *p_sctp_peeloff = NULL; #else -static int (*p_sctp_bindx)(int sd, struct sockaddr *addrs, - int addrcnt, int flags) = NULL; -static int (*p_sctp_peeloff)(int sd, sctp_assoc_t assoc_id) = NULL; +static int (*p_sctp_peeloff) + (int sd, sctp_assoc_t assoc_id) = NULL; +#endif + +#if defined(__GNUC__) && defined(HAVE_SCTP_GETLADDRS) +static typeof(sctp_getladdrs) *p_sctp_getladdrs = NULL; +#else +static int (*p_sctp_getladdrs) + (int sd, sctp_assoc_t assoc_id, struct sockaddr **ss) = NULL; +#endif + +#if defined(__GNUC__) && defined(HAVE_SCTP_FREELADDRS) +static typeof(sctp_freeladdrs) *p_sctp_freeladdrs = NULL; +#else +static void (*p_sctp_freeladdrs)(struct sockaddr *addrs) = NULL; +#endif + +#if defined(__GNUC__) && defined(HAVE_SCTP_GETPADDRS) +static typeof(sctp_getpaddrs) *p_sctp_getpaddrs = NULL; +#else +static int (*p_sctp_getpaddrs) + (int sd, sctp_assoc_t assoc_id, struct sockaddr **ss) = NULL; +#endif + +#if defined(__GNUC__) && defined(HAVE_SCTP_FREEPADDRS) +static typeof(sctp_freepaddrs) *p_sctp_freepaddrs = NULL; +#else +static void (*p_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; #endif #endif /* #if defined(HAVE_SCTP_H) */ @@ -601,7 +632,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_F_BUSY 0x0080 #define INET_F_MULTI_CLIENT 0x0100 /* Multiple clients for one descriptor, i.e. multi-accept */ -/* One numberspace for *_REC_* so if an e.g UDP request is issued +/* One numberspace for *_REQ_* so if an e.g UDP request is issued ** for a TCP socket, the driver can protest. */ #define INET_REQ_OPEN 1 @@ -632,6 +663,8 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_REQ_ACCEPT 26 #define INET_REQ_LISTEN 27 #define INET_REQ_IGNOREFD 28 +#define INET_REQ_GETLADDRS 29 +#define INET_REQ_GETPADDRS 30 /* TCP requests */ /* #define TCP_REQ_ACCEPT 40 MOVED */ @@ -1441,8 +1474,7 @@ static int load_ip_address(ErlDrvTermData* spec, int i, int family, char* buf) #ifdef HAVE_SCTP /* For SCTP, we often need to return {IP, Port} tuples: */ -static int inet_get_address - (int family, char* dst, inet_address* src, unsigned int* len); +static int inet_get_address(char* dst, inet_address* src, unsigned int* len); #define LOAD_IP_AND_PORT_CNT \ (8*LOAD_INT_CNT + LOAD_TUPLE_CNT + LOAD_INT_CNT + LOAD_TUPLE_CNT) @@ -1457,8 +1489,7 @@ static int load_ip_and_port unsigned int len = sizeof(struct sockaddr_storage); unsigned int alen = len; char abuf [len]; - int res = - inet_get_address(desc->sfamily, abuf, (inet_address*) addr, &alen); + int res = inet_get_address(abuf, (inet_address*) addr, &alen); ASSERT(res==0); res = 0; /* Now "abuf" contains: Family(1b), Port(2b), IP(4|16b) */ @@ -3712,9 +3743,27 @@ static int inet_init() /* Check the size of SCTP AssocID -- currently both this driver and the Erlang part require 32 bit: */ ASSERT(sizeof(sctp_assoc_t)==ASSOC_ID_LEN); -# if defined(HAVE_SCTP_BINDX) && defined (HAVE_SCTP_PEELOFF) +# if defined(HAVE_SCTP_BINDX) p_sctp_bindx = sctp_bindx; +# if defined(HAVE_SCTP_PEELOFF) p_sctp_peeloff = sctp_peeloff; +# else + p_sctp_peeloff = NULL; +# endif +# if defined(HAVE_SCTP_GETLADDRS) && defined(HAVE_SCTP_FREELADDRS) + p_sctp_getladdrs = sctp_getladdrs; + p_sctp_freeladdrs = sctp_freeladdrs; +# else + p_sctp_getladdrs = NULL; + p_sctp_freeladdrs = NULL; +# endif +# if defined(HAVE_SCTP_GETPADDRS) && defined(HAVE_SCTP_FREEPADDRS) + p_sctp_getpaddrs = sctp_getpaddrs; + p_sctp_freepaddrs = sctp_freepaddrs; +# else + p_sctp_getpaddrs = NULL; + p_sctp_freepaddrs = NULL; +# endif inet_init_sctp(); add_driver_entry(&sctp_inet_driver_entry); # else @@ -3729,12 +3778,36 @@ static int inet_init() void *ptr; if (erts_sys_ddll_sym(h_libsctp, "sctp_bindx", &ptr) == 0) { p_sctp_bindx = ptr; - inet_init_sctp(); - add_driver_entry(&sctp_inet_driver_entry); if (erts_sys_ddll_sym(h_libsctp, "sctp_peeloff", &ptr) == 0) { p_sctp_peeloff = ptr; } + else p_sctp_peeloff = NULL; + if (erts_sys_ddll_sym(h_libsctp, "sctp_getladdrs", &ptr) == 0) { + p_sctp_getladdrs = ptr; + } + else p_sctp_getladdrs = NULL; + if (erts_sys_ddll_sym(h_libsctp, "sctp_freeladdrs", &ptr) == 0) { + p_sctp_freeladdrs = ptr; + } + else { + p_sctp_freeladdrs = NULL; + p_sctp_getladdrs = NULL; + } + if (erts_sys_ddll_sym(h_libsctp, "sctp_getpaddrs", &ptr) == 0) { + p_sctp_getpaddrs = ptr; + } + else p_sctp_getpaddrs = NULL; + if (erts_sys_ddll_sym(h_libsctp, "sctp_freepaddrs", &ptr) == 0) { + p_sctp_freepaddrs = ptr; + } + else { + p_sctp_freepaddrs = NULL; + p_sctp_getpaddrs = NULL; + } + inet_init_sctp(); + add_driver_entry(&sctp_inet_driver_entry); } + else p_sctp_bindx = NULL; } } # endif @@ -3889,10 +3962,12 @@ static char *inet_set_faddress(int family, inet_address* dst, ** and *len is the length of dst on return ** (suitable to deliver to erlang) */ -static int inet_get_address(int family, char* dst, inet_address* src, unsigned int* len) +static int inet_get_address(char* dst, inet_address* src, unsigned int* len) { + int family; short port; + family = src->sa.sa_family; if ((family == AF_INET) && (*len >= sizeof(struct sockaddr_in))) { dst[0] = INET_AF_INET; port = sock_ntohs(src->sai.sin_port); @@ -3914,6 +3989,75 @@ static int inet_get_address(int family, char* dst, inet_address* src, unsigned i return -1; } +/* Same as the above, but take family from the address structure, +** and advance the address pointer to the next address +** according to the size of the current, +** and return the resulting encoded size +*/ +static int inet_address_to_erlang(char *dst, inet_address **src) { + short port; + + switch ((*src)->sa.sa_family) { + case AF_INET: + if (dst) { + dst[0] = INET_AF_INET; + port = sock_ntohs((*src)->sai.sin_port); + put_int16(port, dst+1); + sys_memcpy(dst+1+2, (char *) &(*src)->sai.sin_addr, 4); + } + (*src) = (inet_address *) (&(*src)->sai + 1); + return 1 + 2 + 4; +#if defined(HAVE_IN6) && defined(AF_INET6) + case AF_INET6: + if (dst) { + dst[0] = INET_AF_INET6; + port = sock_ntohs((*src)->sai6.sin6_port); + put_int16(port, dst+1); + VALGRIND_MAKE_MEM_DEFINED(&(*src)->sai6.sin6_addr,16); /* false undefs from syscall sctp_get[lp]addrs */ + sys_memcpy(dst+1+2, (char *) &(*src)->sai6.sin6_addr, 16); + } + (*src) = (inet_address *) (&(*src)->sai6 + 1); + return 1 + 2 + 16; +#endif + default: + return -1; + } +} + +/* Encode n encoded addresses from addrs in the result buffer +*/ +static ErlDrvSizeT reply_inet_addrs +(int n, inet_address *addrs, char **rbuf, ErlDrvSizeT rsize) { + inet_address *ia; + int i, s; + ErlDrvSizeT rlen; + + if (IS_SOCKET_ERROR(n)) return ctl_error(sock_errno(), rbuf, rsize); + if (n == 0) return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); + + /* Calculate result length */ + rlen = 1; + ia = addrs; + for (i = 0; i < n; i++) { + s = inet_address_to_erlang(NULL, &ia); + if (s < 0) break; + rlen += s; + } + + if (rlen > rsize) (*rbuf) = ALLOC(rlen); + + (*rbuf)[0] = INET_REP_OK; + rlen = 1; + ia = addrs; + for (i = 0; i < n; i++) { + s = inet_address_to_erlang((*rbuf)+rlen, &ia); + if (s < 0) break; + rlen += s; + } + + return rlen; +} + static void desc_close(inet_descriptor* desc) { if (desc->s != INVALID_SOCKET) { @@ -7978,6 +8122,39 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, return ctl_reply(INET_REP_OK, tbuf, strlen(tbuf), rbuf, rsize); } + case INET_REQ_GETPADDRS: { + DEBUGF(("inet_ctl(%ld): INET_GETPADDRS\r\n", (long)desc->port)); + + if (len != 4) return ctl_error(EINVAL, rbuf, rsize); + + if (! IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); + if (! IS_BOUND(desc)) return ctl_xerror(EXBADSEQ, rbuf, rsize); + +#ifdef HAVE_SCTP + if (IS_SCTP(desc) && p_sctp_getpaddrs) { + struct sockaddr *sa; + Uint32 assoc_id; + int n; + ErlDrvSizeT rlen; + + assoc_id = get_int32(buf); + n = p_sctp_getpaddrs(desc->s, assoc_id, &sa); + rlen = reply_inet_addrs(n, (inet_address *) sa, rbuf, rsize); + if (n > 0) p_sctp_freepaddrs(sa); + return rlen; + } +#endif + { /* Fallback to sock_peer */ + inet_address addr; + unsigned int sz; + int i; + + sz = sizeof(addr); + i = sock_peer(desc->s, (struct sockaddr *) &addr, &sz); + return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize); + } + } + case INET_REQ_PEER: { /* get peername */ char tbuf[sizeof(inet_address)]; inet_address peer; @@ -7993,7 +8170,7 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, if (IS_SOCKET_ERROR(sock_peer(desc->s, (struct sockaddr*)ptr,&sz))) return ctl_error(sock_errno(), rbuf, rsize); } - if (inet_get_address(desc->sfamily, tbuf, ptr, &sz) < 0) + if (inet_get_address(tbuf, ptr, &sz) < 0) return ctl_error(EINVAL, rbuf, rsize); return ctl_reply(INET_REP_OK, tbuf, sz, rbuf, rsize); } @@ -8014,6 +8191,39 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, } } + case INET_REQ_GETLADDRS: { + DEBUGF(("inet_ctl(%ld): INET_GETLADDRS\r\n", (long)desc->port)); + + if (len != 4) return ctl_error(EINVAL, rbuf, rsize); + + if (! IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); + if (! IS_BOUND(desc)) return ctl_xerror(EXBADSEQ, rbuf, rsize); + +#ifdef HAVE_SCTP + if (IS_SCTP(desc) && p_sctp_getladdrs) { + struct sockaddr *sa; + Uint32 assoc_id; + int n; + ErlDrvSizeT rlen; + + assoc_id = get_int32(buf); + n = p_sctp_getladdrs(desc->s, assoc_id, &sa); + rlen = reply_inet_addrs(n, (inet_address *) sa, rbuf, rsize); + if (n > 0) p_sctp_freeladdrs(sa); + return rlen; + } +#endif + { /* Fallback to sock_name */ + inet_address addr; + unsigned int sz; + int i; + + sz = sizeof(addr); + i = sock_name(desc->s, (struct sockaddr *) &addr, &sz); + return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize); + } + } + case INET_REQ_NAME: { /* get sockname */ char tbuf[sizeof(inet_address)]; inet_address name; @@ -8030,7 +8240,7 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, if (IS_SOCKET_ERROR(sock_name(desc->s, (struct sockaddr*)ptr, &sz))) return ctl_error(sock_errno(), rbuf, rsize); } - if (inet_get_address(desc->sfamily, tbuf, ptr, &sz) < 0) + if (inet_get_address(tbuf, ptr, &sz) < 0) return ctl_error(EINVAL, rbuf, rsize); return ctl_reply(INET_REP_OK, tbuf, sz, rbuf, rsize); } @@ -10869,7 +11079,7 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event) inet_input_count(desc, n); udesc->i_ptr += n; - inet_get_address(desc->sfamily, abuf, &other, &len); + inet_get_address(abuf, &other, &len); /* Copy formatted address to the buffer allocated; "len" is the actual length which must be <= than the original reserved. This means that the addr + data in the buffer are contiguous, diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam Binary files differindex deed86816e..3870e41e67 100644 --- a/erts/preloaded/ebin/prim_inet.beam +++ b/erts/preloaded/ebin/prim_inet.beam diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index 69eed716ff..430313a459 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -41,8 +41,8 @@ getifaddrs/1, getiflist/1, ifget/3, ifset/3, gethostname/1]). -export([getservbyname/3, getservbyport/3]). --export([peername/1, setpeername/2]). --export([sockname/1, setsockname/2]). +-export([peername/1, setpeername/2, peernames/1, peernames/2]). +-export([sockname/1, setsockname/2, socknames/1, socknames/2]). -export([attach/1, detach/1]). -include("inet_sctp.hrl"). @@ -576,6 +576,36 @@ setpeername(S, undefined) when is_port(S) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% +%% PEERNAMES(insock()) -> {ok, [{IP, Port}, ...]} | {error, Reason} +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +peernames(S) when is_port(S) -> + peernames(S, undefined). + +peernames(S, #sctp_assoc_change{assoc_id=AssocId}) when is_port(S) -> + peernames(S, AssocId); +peernames(S, AssocId) + when is_port(S), is_integer(AssocId); + is_port(S), AssocId =:= undefined -> + Q = get, + Type = [[sctp_assoc_id,0]], + case type_value(Q, Type, AssocId) of + true -> + case ctl_cmd + (S, ?INET_REQ_GETPADDRS, + enc_value(Q, Type, AssocId)) of + {ok,Addrs} -> + {ok,get_addrs(Addrs)}; + Error -> + Error + end; + false -> + {error,einval} + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% %% SOCKNAME(insock()) -> {ok, {IP, Port}} | {error, Reason} %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -601,6 +631,36 @@ setsockname(S, undefined) when is_port(S) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% +%% SOCKNAMES(insock()) -> {ok, [{IP, Port}, ...]} | {error, Reason} +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +socknames(S) when is_port(S) -> + socknames(S, undefined). + +socknames(S, #sctp_assoc_change{assoc_id=AssocId}) when is_port(S) -> + socknames(S, AssocId); +socknames(S, AssocId) + when is_port(S), is_integer(AssocId); + is_port(S), AssocId =:= undefined -> + Q = get, + Type = [[sctp_assoc_id,0]], + case type_value(Q, Type, AssocId) of + true -> + case ctl_cmd + (S, ?INET_REQ_GETLADDRS, + enc_value(Q, Type, AssocId)) of + {ok,Addrs} -> + {ok,get_addrs(Addrs)}; + Error -> + Error + end; + false -> + {error,einval} + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% %% SETOPT(insock(), Opt, Value) -> ok | {error, Reason} %% SETOPTS(insock(), [{Opt,Value}]) -> ok | {error, Reason} %% @@ -2225,6 +2285,12 @@ ip6_to_bytes({A,B,C,D,E,F,G,H}) -> [?int16(A), ?int16(B), ?int16(C), ?int16(D), ?int16(E), ?int16(F), ?int16(G), ?int16(H)]. +get_addrs([]) -> + []; +get_addrs([F,P1,P0|Addr]) -> + {IP,Addrs} = get_ip(F, Addr), + [{IP,?u16(P1, P0)}|get_addrs(Addrs)]. + get_ip(?INET_AF_INET, Addr) -> get_ip4(Addr); get_ip(?INET_AF_INET6, Addr) -> get_ip6(Addr). |