summaryrefslogtreecommitdiff
path: root/erts/preloaded/src/prim_inet.erl
diff options
context:
space:
mode:
Diffstat (limited to 'erts/preloaded/src/prim_inet.erl')
-rw-r--r--erts/preloaded/src/prim_inet.erl227
1 files changed, 182 insertions, 45 deletions
diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl
index ff083d9fc1..c6274f89d6 100644
--- a/erts/preloaded/src/prim_inet.erl
+++ b/erts/preloaded/src/prim_inet.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2021. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2022. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -28,7 +28,7 @@
-export([open/3, open/4, fdopen/4, fdopen/5, close/1]).
-export([bind/3, listen/1, listen/2, peeloff/2]).
--export([connect/3, connect/4, async_connect/4]).
+-export([connect/3, connect/4, async_connect/4, connectx/2, connectx/3]).
-export([accept/1, accept/2, accept/3, async_accept/2]).
-export([shutdown/2]).
-export([send/2, send/3, sendto/4, sendmsg/3, sendfile/4]).
@@ -332,7 +332,7 @@ bindx_check_addrs([Addr|Addrs]) ->
bindx_check_addrs([]) ->
true.
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% CONNECT(insock(), IP, Port [,Timeout]) -> ok | {error, Reason}
%%
@@ -411,6 +411,41 @@ async_connect0(S, Addr, Time) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
+%% CONNECTX(insock(), IPs, Port) -> {ok, AssocId} | {error, Reason}
+%% CONNECTX(insock(), SockAddrs) -> {ok, AssocId} | {error, Reason}
+%%
+%% For SCTP sockets only
+%%
+%% connect the insock() to the addresses given by IPs and Port or SockAddrs
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+connectx(S, IPs, Port) ->
+ connectx(S, {IPs, Port}).
+
+connectx(S, AddrList) ->
+ case type_value(set, addr_list, AddrList) of
+ true ->
+ connectx0(S, AddrList);
+ false ->
+ {error, einval}
+ end.
+
+
+connectx0(S, Addrs) ->
+ Args = [enc_time(-1),enc_value(set, addr_list, Addrs)],
+ case ctl_cmd(S, ?INET_REQ_CONNECT, Args) of
+ {ok, [R1,R0]} ->
+ Ref = ?u16(R1,R0),
+ receive
+ {inet_async, S, Ref, Status} ->
+ Status
+ end;
+ Error ->
+ Error
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
%% ACCEPT(insock() [,Timeout][,FamilyOpts] ) -> {ok,insock()} | {error, Reason}
%%
%% accept incoming connection on listen socket
@@ -1521,6 +1556,7 @@ enc_opt(show_econnreset) -> ?INET_LOPT_TCP_SHOW_ECONNRESET;
enc_opt(line_delimiter) -> ?INET_LOPT_LINE_DELIM;
enc_opt(raw) -> ?INET_OPT_RAW;
enc_opt(bind_to_device) -> ?INET_OPT_BIND_TO_DEVICE;
+enc_opt(debug) -> ?INET_OPT_DEBUG;
% Names of SCTP opts:
enc_opt(sctp_rtoinfo) -> ?SCTP_OPT_RTOINFO;
enc_opt(sctp_associnfo) -> ?SCTP_OPT_ASSOCINFO;
@@ -1588,6 +1624,7 @@ dec_opt(?INET_LOPT_TCP_SHOW_ECONNRESET) -> show_econnreset;
dec_opt(?INET_LOPT_LINE_DELIM) -> line_delimiter;
dec_opt(?INET_OPT_RAW) -> raw;
dec_opt(?INET_OPT_BIND_TO_DEVICE) -> bind_to_device;
+dec_opt(?INET_OPT_DEBUG) -> debug;
dec_opt(I) when is_integer(I) -> undefined.
@@ -1605,7 +1642,7 @@ dec_opt(I) when is_integer(I) -> undefined.
%% The use of [] and [[Type,Default]] is commented out in enc_value/2
%% and type_value/2 below since they are only used in record fields.
%% And record fields does not call enc_value/2 nor type_value/2.
-%% Anyone introducing these metatypes otherwhere will have to activate
+%% Anyone introducing these metatypes elsewhere will have to activate
%% those clauses in enc_value/2 and type_value/2. You have been warned!
type_opt(get, raw) -> [{[int],[int],[binary_or_uint]}];
@@ -1647,9 +1684,9 @@ type_opt_1(ipv6_v6only) -> bool;
%% multicast
type_opt_1(multicast_ttl) -> int;
type_opt_1(multicast_loop) -> bool;
-type_opt_1(multicast_if) -> ip;
-type_opt_1(add_membership) -> {ip,ip};
-type_opt_1(drop_membership) -> {ip,ip};
+type_opt_1(multicast_if) -> mif;
+type_opt_1(add_membership) -> membership;
+type_opt_1(drop_membership) -> membership;
%% driver options
type_opt_1(header) -> uint;
type_opt_1(buffer) -> int;
@@ -1696,6 +1733,7 @@ type_opt_1(read_packets) -> uint;
type_opt_1(netns) -> binary;
type_opt_1(show_econnreset) -> bool;
type_opt_1(bind_to_device) -> binary;
+type_opt_1(debug) -> bool;
%%
%% SCTP options (to be set). If the type is a record type, the corresponding
%% record signature is returned, otherwise, an "elementary" type tag
@@ -1883,7 +1921,23 @@ type_value_2(uint16, X) when X band 16#ffff =:= X -> true;
type_value_2(uint8, X) when X band 16#ff =:= X -> true;
type_value_2(time, infinity) -> true;
type_value_2(time, X) when is_integer(X), X >= 0 -> true;
-type_value_2(ip,{A,B,C,D}) when ?ip(A,B,C,D) -> true;
+%% type_value_2(ip,{A,B,C,D}) when ?ip(A,B,C,D) -> true;
+type_value_2(mif,{A,B,C,D}) when ?ip(A,B,C,D) -> true;
+type_value_2(mif,Idx) when is_integer(Idx) -> true;
+type_value_2(membership,{{A1,B1,C1,D1}, {A2,B2,C2,D2}})
+ when ?ip(A1,B1,C1,D1) andalso ?ip(A2,B2,C2,D2) -> true;
+type_value_2(membership,{{A1,B1,C1,D1}, any})
+ when ?ip(A1,B1,C1,D1) -> true;
+type_value_2(membership,{{A1,B1,C1,D1}, {A2,B2,C2,D2}, Idx})
+ when ?ip(A1,B1,C1,D1) andalso
+ ?ip(A2,B2,C2,D2) andalso
+ is_integer(Idx) -> true;
+type_value_2(membership,{{A1,B1,C1,D1}, any, Idx})
+ when ?ip(A1,B1,C1,D1) andalso
+ is_integer(Idx) -> true;
+type_value_2(membership,{{A,B,C,D,E,F,G,H}, Idx})
+ when ?ip6(A,B,C,D,E,F,G,H) andalso
+ is_integer(Idx) -> true;
%%
type_value_2(addr, {any,Port}) ->
type_value_2(uint16, Port);
@@ -1907,15 +1961,19 @@ type_value_2(addr, {inet6,{{A,B,C,D,E,F,G,H},Port}})
when ?ip6(A,B,C,D,E,F,G,H) ->
type_value_2(uint16, Port);
type_value_2(addr, #{family := inet,
- addr := {A,B,C,D},
- port := Port})
- when ?ip(A,B,C,D) ->
- type_value_2(uint16, Port);
+ addr := Addr,
+ port := Port}) ->
+ (Addr =:= any orelse
+ Addr =:= loopback orelse
+ ?ip(Addr))
+ andalso type_value_2(uint16, Port);
type_value_2(addr, #{family := inet6,
- addr := {A,B,C,D,E,F,G,H},
- port := Port})
- when ?ip6(A,B,C,D,E,F,G,H) ->
- type_value_2(uint16, Port);
+ addr := Addr,
+ port := Port}) ->
+ (Addr =:= any orelse
+ Addr =:= loopback orelse
+ ?ip6(Addr))
+ andalso type_value_2(uint16, Port);
type_value_2(addr, {local,Addr}) ->
if
is_binary(Addr) ->
@@ -1936,6 +1994,16 @@ type_value_2(addr, {local,Addr}) ->
false
end
end;
+type_value_2(addr_list, [_|_]=SockAddrs)
+ when length(SockAddrs) =< 255 ->
+ lists:all(fun(SockAddr) -> type_value_2(addr, SockAddr) end, SockAddrs);
+type_value_2(addr_list, {[_|_] = IPs, Port})
+ when length(IPs) =< 255 ->
+ lists:all(fun({A,B,C,D}) when ?ip(A,B,C,D) -> true;
+ ({A,B,C,D,E,F,G,H}) when ?ip6(A,B,C,D,E,F,G,H) -> true;
+ (_) -> false
+ end, IPs)
+ andalso type_value_2(uint16, Port);
%%
type_value_2(ether,[X1,X2,X3,X4,X5,X6])
when ?ether(X1,X2,X3,X4,X5,X6) -> true;
@@ -2029,21 +2097,58 @@ enc_value_tuple(_, _, _, _) -> [].
%%
%% Encoding of option VALUES:
%%
-enc_value_2(bool, true) -> [0,0,0,1];
-enc_value_2(bool, false) -> [0,0,0,0];
-enc_value_2(bool8, true) -> [1];
-enc_value_2(bool8, false) -> [0];
-enc_value_2(int, Val) -> ?int32(Val);
-enc_value_2(uint, Val) -> ?int32(Val);
-enc_value_2(uint32, Val) -> ?int32(Val);
-enc_value_2(uint24, Val) -> ?int24(Val);
-enc_value_2(uint16, Val) -> ?int16(Val);
-enc_value_2(uint8, Val) -> ?int8(Val);
-enc_value_2(time, infinity) -> ?int32(-1);
-enc_value_2(time, Val) -> ?int32(Val);
-enc_value_2(ip,{A,B,C,D}) -> [A,B,C,D];
-enc_value_2(ip, any) -> [0,0,0,0];
-enc_value_2(ip, loopback) -> [127,0,0,1];
+enc_value_2(bool, true) -> [0,0,0,1];
+enc_value_2(bool, false) -> [0,0,0,0];
+enc_value_2(bool8, true) -> [1];
+enc_value_2(bool8, false) -> [0];
+enc_value_2(int, Val) -> ?int32(Val);
+enc_value_2(uint, Val) -> ?int32(Val);
+enc_value_2(uint32, Val) -> ?int32(Val);
+enc_value_2(uint24, Val) -> ?int24(Val);
+enc_value_2(uint16, Val) -> ?int16(Val);
+enc_value_2(uint8, Val) -> ?int8(Val);
+enc_value_2(time, infinity) -> ?int32(-1);
+enc_value_2(time, Val) -> ?int32(Val);
+%% enc_value_2(ip, IP)
+%% when (tuple_size(IP) =:= 4) -> ip4_to_bytes(IP);
+%% enc_value_2(ip, any) -> ip4_any();
+%% enc_value_2(ip, loopback) -> ip4_loopback();
+enc_value_2(mif, IP)
+ when (tuple_size(IP) =:= 4) -> ip4_to_bytes(IP);
+enc_value_2(mif, Idx)
+ when is_integer(Idx) -> ?int32(Idx);
+enc_value_2(membership, {IP1, IP2})
+ when (tuple_size(IP1) =:= 4) andalso
+ (tuple_size(IP2) =:= 4) ->
+ enc_value_2(membership, {IP1, IP2, 0});
+enc_value_2(membership, {IP1, IP2})
+ when (tuple_size(IP1) =:= 4) andalso
+ (IP2 =:= any) ->
+ enc_value_2(membership, {IP1, IP2, 0});
+%% enc_value_2(membership, {IP1, any = _IP2})
+%% when (tuple_size(IP1) =:= 4) ->
+%% [?INET_AF_INET, ?int32(0), ip4_to_bytes(IP1), ip4_any()];
+enc_value_2(membership, {IP1, IP2, Idx})
+ when (tuple_size(IP1) =:= 4) andalso
+ (tuple_size(IP2) =:= 4) andalso
+ is_integer(Idx) ->
+ %% The reason for turning this thing around (the interface
+ %% before the two address'es) so that we as much as possible
+ %% "look like" IPv6...se below
+ [?int32(?INET_AF_INET), ?int32(Idx), ip4_to_bytes(IP1), ip4_to_bytes(IP2)];
+enc_value_2(membership, {IP1, any = _IP2, Idx})
+ when (tuple_size(IP1) =:= 4) andalso
+ is_integer(Idx) ->
+ [?int32(?INET_AF_INET), ?int32(Idx), ip4_to_bytes(IP1), ip4_any()];
+enc_value_2(membership, {IP, Idx})
+ when (tuple_size(IP) =:= 8) andalso
+ is_integer(Idx) ->
+ %% The reason for turning this thing around (the interface
+ %% before the address) is because of the inet-driver (it reads out a
+ %% 32-bit value for *all* options, so we might as well put a 32-bit
+ %% value "first".
+ [?int32(?INET_AF_INET6), ?int32(Idx), ip6_to_bytes(IP)];
+
%%
enc_value_2(addr, {any,Port}) ->
[?INET_AF_ANY|?int16(Port)];
@@ -2055,27 +2160,19 @@ enc_value_2(addr, {IP,Port}) when tuple_size(IP) =:= 8 ->
[?INET_AF_INET6,?int16(Port),ip6_to_bytes(IP),?int32(0),?int32(0)];
enc_value_2(addr, #{family := inet,
addr := IP,
- port := Port}) when (tuple_size(IP) =:= 4) ->
+ port := Port}) ->
[?INET_AF_INET,?int16(Port)|ip4_to_bytes(IP)];
enc_value_2(addr, #{family := inet6,
addr := IP,
port := Port,
flowinfo := FlowInfo,
- scope_id := ScopeID}) when (tuple_size(IP) =:= 8) ->
+ scope_id := ScopeID}) ->
[?INET_AF_INET6,?int16(Port),ip6_to_bytes(IP),?int32(FlowInfo),?int32(ScopeID)];
enc_value_2(addr, {File,_}) when is_list(File); is_binary(File) ->
[?INET_AF_LOCAL,iolist_size(File)|File];
%%
-enc_value_2(addr, {inet,{any,Port}}) ->
- [?INET_AF_INET,?int16(Port)|ip4_to_bytes({0,0,0,0})];
-enc_value_2(addr, {inet,{loopback,Port}}) ->
- [?INET_AF_INET,?int16(Port)|ip4_to_bytes({127,0,0,1})];
enc_value_2(addr, {inet,{IP,Port}}) ->
[?INET_AF_INET,?int16(Port)|ip4_to_bytes(IP)];
-enc_value_2(addr, {inet6,{any,Port}}) ->
- [?INET_AF_INET6,?int16(Port),ip6_to_bytes({0,0,0,0,0,0,0,0}),?int32(0),?int32(0)];
-enc_value_2(addr, {inet6,{loopback,Port}}) ->
- [?INET_AF_INET6,?int16(Port),ip6_to_bytes({0,0,0,0,0,0,0,1}),?int32(0),?int32(0)];
enc_value_2(addr, {inet6,{IP,Port}}) ->
[?INET_AF_INET6,?int16(Port),ip6_to_bytes(IP),?int32(0),?int32(0)];
enc_value_2(addr, {local,Addr}) ->
@@ -2091,6 +2188,12 @@ enc_value_2(addr, {local,Addr}) ->
Addr, file:native_name_encoding())
end,
[?INET_AF_LOCAL,byte_size(Bin),Bin];
+enc_value_2(addr_list, {IPs, Port}) ->
+ [?INET_AF_LIST, length(IPs) |
+ [enc_value_2(addr, {IP, Port}) || IP <- IPs]];
+enc_value_2(addr_list, SockAddrs) ->
+ [?INET_AF_LIST, length(SockAddrs) |
+ [enc_value_2(addr, SockAddr) || SockAddr <- SockAddrs]];
%%
enc_value_2(ether, [_,_,_,_,_,_]=Xs) -> Xs;
enc_value_2(sockaddr, any) ->
@@ -2125,7 +2228,7 @@ enc_value_2(binary_or_uint,Datum) when is_integer(Datum) ->
%%
-%% Decoding of option VALUES receved from "getopt":
+%% Decoding of option VALUES received from "getopt":
%% NOT required for SCTP, as it always returns ready terms, not lists:
%%
dec_value(bool, [0,0,0,0|T]) -> {false,T};
@@ -2145,7 +2248,15 @@ dec_value(time, [X3,X2,X1,X0|T]) ->
-1 -> {infinity, T};
Val -> {Val, T}
end;
-dec_value(ip, [A,B,C,D|T]) -> {{A,B,C,D}, T};
+%% dec_value(ip, [A,B,C,D|T]) -> {{A,B,C,D}, T};
+dec_value(mif, [A,B,C,D, X3,X2,X1,X0|T]) ->
+ Domain = ?i32(X3, X2, X1, X0),
+ case Domain of
+ ?INET_AF_INET ->
+ {{A,B,C,D}, T};
+ ?INET_AF_INET6 ->
+ {?i32(A,B,C,D), T}
+ end;
%% dec_value(ether, [X1,X2,X3,X4,X5,X6|T]) -> {[X1,X2,X3,X4,X5,X6],T};
dec_value(sockaddr, [X|T]) ->
get_ip(X, T);
@@ -2451,7 +2562,8 @@ decode_ifopts([B | Buf], Acc) ->
undefined ->
{error, einval};
Opt ->
- {Val,T} = dec_value(type_ifopt(Opt), Buf),
+ OptType = type_ifopt(Opt),
+ {Val,T} = dec_value(OptType, Buf),
decode_ifopts(T, [{Opt,Val} | Acc])
end;
decode_ifopts(_,Acc) -> {ok,Acc}.
@@ -2518,7 +2630,7 @@ dec_subs([]) -> [].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
-%% handle statictics options
+%% handle statistics options
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -2731,13 +2843,38 @@ utf8_to_characters(Bs, U, 0) ->
utf8_to_characters([B|Bs], U, N) when ((B band 16#3F) bor 16#80) =:= B ->
utf8_to_characters(Bs, (U bsl 6) bor (B band 16#3F), N-1).
+ip4_to_bytes(any) ->
+ ip4_any();
+ip4_to_bytes(loopback) ->
+ ip4_loopback();
ip4_to_bytes({A,B,C,D}) ->
[A band 16#ff, B band 16#ff, C band 16#ff, D band 16#ff].
+ip4_any() -> [0,0,0,0].
+ip4_loopback() -> [127,0,0,1].
+
+
+ip6_to_bytes(any) ->
+ ip6_any();
+ip6_to_bytes(loopback) ->
+ ip6_loopback();
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)].
+ip6_any() ->
+ Z1 = ?int16(0),
+ Z2 = [Z1, Z1],
+ Z3 = [Z2, Z2],
+ [Z3, Z3].
+
+ip6_loopback() ->
+ Z1 = ?int16(0),
+ Z2 = [Z1, Z1],
+ Z3 = [Z2, Z2],
+ [Z3, Z2, Z1, ?int16(1)].
+
+
get_addrs([]) ->
[];
get_addrs([F|Addrs]) ->