From 3ca71520bfb664f0ea809ffdf41505936e4d5e90 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 22 Mar 2018 17:57:15 +0100 Subject: [socket-nif] preliminary version of the new socket interface (nififying) --- erts/preloaded/src/Makefile | 1 + erts/preloaded/src/init.erl | 1 + erts/preloaded/src/socket.erl | 878 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 880 insertions(+) create mode 100644 erts/preloaded/src/socket.erl (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile index 4333f6643a..aa9390b038 100644 --- a/erts/preloaded/src/Makefile +++ b/erts/preloaded/src/Makefile @@ -39,6 +39,7 @@ PRE_LOADED_ERL_MODULES = \ prim_buffer \ prim_file \ prim_inet \ + socket \ zlib \ prim_zip \ otp_ring0 \ diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index 253fcf7a1f..303301cbff 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -202,6 +202,7 @@ boot(BootArgs) -> %% Load the static nifs zlib:on_load(), + socket:on_load(), erl_tracer:on_load(), prim_buffer:on_load(), prim_file:on_load(), diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl new file mode 100644 index 0000000000..f8e3d0419a --- /dev/null +++ b/erts/preloaded/src/socket.erl @@ -0,0 +1,878 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2018-2018. 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. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(socket). + +%% Administrative and "global" utility functions +-export([ + on_load/0, on_load/1, on_load/2, + info/0 + ]). + +-export([ + open/2, open/3, open/4, + bind/2, bind/3, + connect/3, + listen/1, listen/2, + accept/1, accept/2, + accept4/1, accept4/2, accept4/3, + + send/2, send/3, sendto/5, + recv/1, recv/2, recvfrom/1, recvfrom/2, + + close/1, + + setopt/3, + getopt/2, + formated_timestamp/0 + ]). + +-export_type([ + domain/0, + type/0, + protocol/0, + socket/0, + + ip_address/0, + ip4_address/0, + ip6_address/0, + port_number/0, + + accept_flags/0, + accept_flag/0, + + send_flags/0, + send_flag/0 + ]). + + +%% We support only a subset of all domains. +-type domain() :: local | inet | inet6. + +%% We support only a subset of all types. +-type type() :: stream | dgram | raw | seqpacket. + +%% We support only a subset of all protocols: +-type protocol() :: ip | tcp | udp | sctp. + +-type ip_address() :: ip4_address() | ip6_address(). + +-type ip4_address() :: {0..255, 0..255, 0..255, 0..255}. + +-type ip6_address() :: + {0..65535, + 0..65535, + 0..65535, + 0..65535, + 0..65535, + 0..65535, + 0..65535, + 0..65535}. + +-type port_number() :: 0..65535. + +-type socket_info() :: map(). +%% -record(socket, {info :: socket_info, +%% ref :: reference()}). +-opaque socket() :: {socket, socket_info(), reference()}. +%% -opaque socket() :: #socket{}. + +-type accept_flags() :: [accept_flag()]. +-type accept_flag() :: nonblock | cloexec. + +-type send_flags() :: [send_flag()]. +-type send_flag() :: confirm | + dontroute | + dontwait | + eor | + more | + nosignal | + oob. + +-type recv_flags() :: [recv_flag()]. +-type recv_flag() :: cmsg_cloexec | + dontwait | + errqueue | + oob | + peek | + trunc | + waitall. + +-type setopt_key() :: foo. +-type getopt_key() :: foo. + +-define(SOCKET_DOMAIN_LOCAL, 1). +-define(SOCKET_DOMAIN_UNIX, ?SOCKET_DOMAIN_LOCAL). +-define(SOCKET_DOMAIN_INET, 2). +-define(SOCKET_DOMAIN_INET6, 3). + +-define(SOCKET_TYPE_STREAM, 1). +-define(SOCKET_TYPE_DGRAM, 2). +-define(SOCKET_TYPE_RAW, 3). +%% -define(SOCKET_TYPE_RDM, 4). +-define(SOCKET_TYPE_SEQPACKET, 5). + +-define(SOCKET_PROTOCOL_IP, 1). +-define(SOCKET_PROTOCOL_TCP, 2). +-define(SOCKET_PROTOCOL_UDP, 3). +-define(SOCKET_PROTOCOL_SCTP, 4). + +-define(SOCKET_LISTEN_BACKLOG_DEFAULT, 5). + +%% Bit numbers (from right). +-define(SOCKET_ACCEPT_FLAG_NONBLOCK, 0). +-define(SOCKET_ACCEPT_FLAG_CLOEXEC, 1). + +-define(SOCKET_ACCEPT_FLAGS_DEFAULT, []). + +-define(SOCKET_SEND_FLAG_CONFIRM, 0). +-define(SOCKET_SEND_FLAG_DONTROUTE, 1). +-define(SOCKET_SEND_FLAG_DONTWAIT, 2). +-define(SOCKET_SEND_FLAG_EOR, 3). +-define(SOCKET_SEND_FLAG_MORE, 4). +-define(SOCKET_SEND_FLAG_NOSIGNAL, 5). +-define(SOCKET_SEND_FLAG_OOB, 6). + +-define(SOCKET_SEND_FLAGS_DEFAULT, []). + +-define(SOCKET_RECV_FLAG_CMSG_CLOEXEC, 0). +-define(SOCKET_RECV_FLAG_DONTWAIT, 1). +-define(SOCKET_RECV_FLAG_ERRQUEUE, 2). +-define(SOCKET_RECV_FLAG_OOB, 3). +-define(SOCKET_RECV_FLAG_PEEK, 4). +-define(SOCKET_RECV_FLAG_TRUNC, 5). +-define(SOCKET_RECV_FLAG_WAITALL, 6). + +-define(SOCKET_RECV_FLAGS_DEFAULT, []). + +-define(SOCKET_SETOPT_KEY_DEBUG, 0). + +-define(SOCKET_GETOPT_KEY_DEBUG, ?SOCKET_SETOPT_KEY_DEBUG). + + + +%% =========================================================================== +%% +%% Administrative and utility API +%% +%% =========================================================================== + +-spec on_load() -> ok. + +%% Should we require that the Extra arg is a map? +on_load() -> + on_load(#{}). + +-spec on_load(Extra) -> ok when + Extra :: maps:map(). + +on_load(Extra) when is_map(Extra) -> + on_load(atom_to_list(?MODULE), Extra). + +-spec on_load(Path, Extra) -> ok when + Path :: string(), + Extra :: maps:map(). + +on_load(Path, Extra) when is_list(Path) andalso is_map(Extra) -> + on_load(nif_is_loaded(), Path, Extra). + +on_load(true, _Path, _Extra) -> + ok; +on_load(false, Path, Extra) -> + ok = erlang:load_nif(Path, maps:put(timestamp, formated_timestamp(), Extra)). + + + +-spec info() -> list(). + +info() -> + nif_info(). + + + +%% =========================================================================== +%% +%% The proper socket API +%% +%% =========================================================================== + +%% open - create an endpoint for communication + +open(Domain, Type) -> + open(Domain, Type, null). + +-spec open(Domain, Type, Protocol) -> {ok, Socket} | {error, Reason} when + Domain :: domain(), + Type :: type(), + Protocol :: null | protocol(), + Socket :: socket(), + Reason :: term(). + +open(Domain, Type, Protocol) -> + open(Domain, Type, Protocol, #{}). + +-spec open(Domain, Type, Protocol, Extra) -> {ok, Socket} | {error, Reason} when + Domain :: domain(), + Type :: type(), + Protocol :: null | protocol(), + Extra :: map(), + Socket :: socket(), + Reason :: term(). + +open(Domain, Type, Protocol0, Extra) when is_map(Extra) -> + try + begin + Protocol = default_protocol(Protocol0, Type), + EDomain = enc_domain(Domain), + EType = enc_type(Domain, Type), + EProtocol = enc_protocol(Type, Protocol), + case nif_open(EDomain, EType, EProtocol, Extra) of + {ok, SockRef} -> + SocketInfo = #{domain => Domain, + type => Type, + protocol => Protocol}, + Socket = {socket, SocketInfo, SockRef}, + {ok, Socket}; + {error, _} = ERROR -> + ERROR + end + end + catch + throw:T -> + T; + error:Reason -> + {error, Reason} + end. + +%% Note that this is just a convenience function for when the protocol was +%% not specified. If its actually specied, then that will be selected. +%% Also, this only works for the some of the type's (stream, dgram and +%% seqpacket). +default_protocol(null, stream) -> tcp; +default_protocol(null, dgram) -> udp; +default_protocol(null, seqpacket) -> sctp; +default_protocol(null, Type) -> throw({error, {no_default_protocol, Type}}); +default_protocol(Protocol, _) -> Protocol. + + +%% bind - bind a name to a socket + +-spec bind(Socket, FileOrAddr) -> ok | {error, Reason} when + Socket :: socket(), + FileOrAddr :: binary() | string() | ip_address() | any | loopback, + Reason :: term(). + +bind(Socket, File) when is_binary(File) -> + if + byte_size(File) =:= 0 -> + {error, einval}; + byte_size(File) =< 255 -> + nif_bind(Socket, File); + true -> + {error, einval} + end; +bind(Socket, File) when is_list(File) andalso (File =/= []) -> + Bin = unicode:characters_to_binary(File, file:native_name_encoding()), + if + byte_size(Bin) =< 255 -> + nif_bind(Socket, Bin); + true -> + {error, einval} + end; +bind(Socket, Addr) when is_tuple(Addr) orelse + (Addr =:= any) orelse + (Addr =:= loopback) -> + bind(Socket, Addr, 0). + + +-spec bind(Socket, Address, Port) -> ok | {ok, NewPort} | {error, Reason} when + Socket :: socket(), + Address :: ip_address() | any | loopback, + Port :: port_number(), + NewPort :: port_number(), + Reason :: term(). + +%% Shall we keep info about domain so that we can verify address? +bind({socket, _, SockRef}, Addr, Port) + when (is_tuple(Addr) andalso + ((size(Addr) =:= 4) orelse (size(Addr) =:= 8))) orelse + ((Addr =:= any) orelse (Addr =:= loopback)) andalso + (is_integer(Port) andalso (Port >= 0)) -> + nif_bind(SockRef, {Addr, Port}). + + +%% connect - initiate a connection on a socket + +-spec connect(Socket, Addr, Port) -> ok | {error, Reason} when + Socket :: socket(), + Addr :: ip_address(), + Port :: port_number(), + Reason :: term(). + +connect(Socket, Addr, Port) -> + connect(Socket, Addr, Port, infinity). + +-spec connect(Socket, Addr, Port, Timeout) -> ok | {error, Reason} when + Socket :: socket(), + Addr :: ip_address(), + Port :: port_number(), + Timeout :: timeout(), + Reason :: term(). + +connect(_Socket, _Addr, _Port, Timeout) + when (is_integer(Timeout) andalso (Timeout =< 0)) -> + {error, timeout}; +connect({socket, _, SockRef}, Addr, Port, Timeout) + when (is_tuple(Addr) andalso + ((size(Addr) =:= 4) orelse (size(Addr) =:= 8))) andalso + (is_integer(Port) andalso (Port >= 0)) andalso + ((Timeout =:= infinity) orelse is_integer(Timeout)) -> + TS = timestamp(Timeout), + case nif_connect(SockRef, Addr, Port) of + ok -> + %% Connected! + ok; + {ok, Ref} -> + %% Connecting... + NewTimeout = next_timeout(TS, Timeout), + receive + {select, SockRef, Ref, ready_output} -> + nif_finalize_connection(SockRef) + after NewTimeout -> + nif_cancel(SockRef, Ref), + {error, timeout} + end; + {error, _} = ERROR -> + ERROR + end. + + +%% listen - listen for connections on a socket + +-spec listen(Socket, Backlog) -> ok | {error, Reason} when + Socket :: socket(), + Backlog :: pos_integer(), + Reason :: term(). + +listen(Socket) -> + listen(Socket, ?SOCKET_LISTEN_BACKLOG_DEFAULT). + +listen({socket, _, SockRef}, Backlog) + when (is_integer(Backlog) andalso (Backlog >= 0)) orelse is_boolean(Backlog) -> + EBacklog = enc_backlog(Backlog), + nif_listen(SockRef, EBacklog). + + + +%% accept, accept4 - accept a connection on a socket + +-spec accept(LSocket, Timeout) -> {ok, Socket} | {error, Reason} when + LSocket :: socket(), + Timeout :: timeout(), + Socket :: socket(), + Reason :: term(). + +accept(Socket) -> + accept(Socket, infinity). + +%% Do we really need this optimization? +accept(_, Timeout) when is_integer(Timeout) andalso (Timeout < 0) -> + {error, timeout}; +accept({socket, _, LSockRef}, Timeout) + when is_integer(Timeout) orelse (Timeout =:= infinity) -> + Ref = make_ref(), + do_accept(LSockRef, Ref, Timeout). + +do_accept(_, _Ref, Timeout) when is_integer(Timeout) andalso (Timeout < 0) -> + {error, timeout}; +do_accept(LSockRef, Ref, Timeout) -> + TS = timestamp(Timeout), + case nif_accept(LSockRef, Ref) of + {ok, SockRef} -> + Socket = {socket, foo, SockRef}, + {ok, Socket}; + {error, eagain} -> + receive + {select, LSockRef, Ref, ready_input} -> + do_accept(LSockRef, make_ref(), next_timeout(TS, Timeout)) + after Timeout -> + %% Shall we cancel the select? How? + nif_cancel(LSockRef, Ref), + flush_select_msgs(LSockRef, Ref), + {error, timeout} + end + end. + +flush_select_msgs(LSRef, Ref) -> + receive + {select, LSRef, Ref, _} -> + flush_select_msgs(LSRef, Ref) + after 0 -> + ok + end. + + +-spec accept4(LSocket, Flags, Timeout) -> {ok, Socket} | {error, Reason} when + LSocket :: socket(), + Flags :: accept_flags(), + Timeout :: timeout(), + Socket :: socket(), + Reason :: term(). + +accept4(LSocket) -> + accept4(LSocket, ?SOCKET_ACCEPT_FLAGS_DEFAULT). + +accept4(LSocket, Flags) when is_list(Flags) -> + accept4(LSocket, Flags, infinity); +accept4(LSocket, Timeout) -> + accept4(LSocket, ?SOCKET_ACCEPT_FLAGS_DEFAULT, Timeout). + +%% Do we really need this optimization? +accept4(_LSocket, _Flags, Timeout) when is_integer(Timeout) andalso (Timeout < 0) -> + {error, timeout}; +accept4({socket, _, LSockRef}, Flags, Timeout) + when is_list(Flags) andalso + (is_integer(Timeout) orelse (Timeout =:= infinity)) -> + EFlags = enc_accept_flags(Flags), + Ref = make_ref(), + do_accept4(LSockRef, EFlags, Ref, Timeout). + +do_accept4(_, _EFlags, _Ref, Timeout) + when is_integer(Timeout) andalso (Timeout < 0) -> + {error, timeout}; +do_accept4(LSockRef, EFlags, Ref, Timeout) -> + TS = timestamp(Timeout), + case nif_accept4(LSockRef, EFlags, Ref) of + {ok, SockRef} -> + Socket = {socket, foo, SockRef}, + {ok, Socket}; + {error, eagain} -> + receive + {select, LSockRef, Ref, ready_input} -> + do_accept4(LSockRef, EFlags, make_ref(), + next_timeout(TS, Timeout)) + after Timeout -> + %% Shall we cancel the select? How? + nif_cancel(LSockRef, Ref), + flush_select_msgs(LSockRef, Ref), + {error, timeout} + end + end. + + + +%% send, sendto, sendmsg - send a message on a socket + +-spec send(Socket, Data, Flags) -> ok | {error, Reason} when + Socket :: socket(), + Data :: binary(), + Flags :: send_flags(), + Reason :: term(). + +send(Socket, Data) -> + send(Socket, Data, ?SOCKET_SEND_FLAGS_DEFAULT). + +send({socket, _, SockRef}, Data, Flags) + when is_binary(Data) andalso is_list(Flags) -> + EFlags = enc_send_flags(Flags), + nif_send(SockRef, Data, EFlags). + + +-spec sendto(Socket, Data, Flags, DestAddr, Port) -> ok | {error, Reason} when + Socket :: socket(), + Data :: binary(), + Flags :: send_flags(), + DestAddr :: ip_address(), + Port :: port_number(), + Reason :: term(). + +sendto({socket, _, SockRef}, Data, Flags, DestAddr, DestPort) + when is_binary(Data) andalso + is_list(Flags) andalso + (is_tuple(DestAddr) andalso + ((size(DestAddr) =:= 4) orelse + (size(DestAddr) =:= 8))) andalso + (is_integer(DestPort) andalso (DestPort >= 0)) -> + EFlags = enc_send_flags(Flags), + nif_sendto(SockRef, Data, EFlags, DestAddr, DestPort). + + +%% -spec sendmsg(Socket, MsgHdr, Flags) -> ok | {error, Reason} when +%% Socket :: socket(), +%% MsgHdr :: msg_header(), +%% Flags :: send_flags(), +%% Reason :: term(). + + + +%% recv, recvfrom, recvmsg - receive a message from a socket + +-spec recv(Socket, Flags) -> {ok, Data} | {error, Reason} when + Socket :: socket(), + Flags :: recv_flags(), + Data :: binary(), + Reason :: term(). + +recv(Socket) -> + recv(Socket, ?SOCKET_RECV_FLAGS_DEFAULT). + +%% WE "may" need a timeout option here... +recv({socket, _, SockRef}, Flags) when is_list(Flags) -> + EFlags = enc_recv_flags(Flags), + nif_recv(SockRef, EFlags). + + +-spec recvfrom(Socket, Flags) -> {ok, Data, SrcAddr, SrcPort} | {error, Reason} when + Socket :: socket(), + Flags :: recv_flags(), + Data :: binary(), + SrcAddr :: ip_address(), + SrcPort :: port_number(), + Reason :: term(). + +recvfrom(Socket) -> + recvfrom(Socket, ?SOCKET_RECV_FLAGS_DEFAULT). + +recvfrom({socket, _, SockRef}, Flags) when is_list(Flags) -> + EFlags = enc_recv_flags(Flags), + nif_recvfrom(SockRef, EFlags). + +%% -spec recvmsg(Socket, [out] MsgHdr, Flags) -> {ok, Data} | {error, Reason} when +%% Socket :: socket(), +%% MsgHdr :: msg_header(), +%% Flags :: recv_flags(), +%% Data :: binary(), +%% Reason :: term(). + + + +%% close - close a file descriptor + +-spec close(Socket) -> ok | {error, Reason} when + Socket :: socket(), + Reason :: term(). + +close({socket, _, SockRef}) -> + nif_close(SockRef). + + + +%% setopt - manipulate individual properties of a socket +%% +%% What properties are valid depend on what kind of socket it is +%% (domain, type and protocol) +%% If its an "invalid" option (or value), we should not crash but return some +%% useful error... +%% + +-spec setopt(Socket, Key, Value) -> ok | {error, Reason} when + Socket :: socket(), + Key :: setopt_key(), + Value :: term(), + Reason :: term(). + +setopt({socket, Info, SockRef}, Key, Value) -> + try + begin + Domain = maps:get(domain, Info), + Type = maps:get(type, Info), + Protocol = maps:get(protocol, Info), + EKey = enc_setopt_key(Key, Domain, Type, Protocol), + EVal = enc_setopt_value(Key, Value, Domain, Type, Protocol), + nif_setopt(SockRef, EKey, EVal) + end + catch + throw:T -> + T; + error:Reason -> + {error, Reason} % Process more? + end. + + +%% getopt - retrieve individual properties of a socket +%% +%% What properties are valid depend on what kind of socket it is +%% (domain, type and protocol). +%% If its an "invalid" option, we should not crash but return some +%% useful error... +%% + +-spec getopt(Socket, Key) -> {ok, Value} | {error, Reason} when + Socket :: socket(), + Key :: getopt_key(), + Value :: term(), + Reason :: term(). + +getopt({socket, Info, SockRef}, Key) -> + try + begin + Domain = maps:get(domain, Info), + Type = maps:get(type, Info), + Protocol = maps:get(protocol, Info), + EKey = enc_getopt_key(Key, Domain, Type, Protocol), + %% We may need to decode the value (for the same reason + %% we needed to encode the value for setopt). + case nif_getopt(SockRef, EKey) of + {ok, EVal} -> + Val = dec_getopt_value(Key, EVal, Domain, Type, Protocol), + {ok, Val}; + {error, _} = ERROR -> + ERROR + end + end + catch + throw:T -> + T; + error:Reason -> + {error, Reason} % Process more? + end. + + + +%% =========================================================================== +%% +%% Encode / decode +%% +%% =========================================================================== + +-spec enc_domain(Domain) -> non_neg_integer() when + Domain :: domain(). + +enc_domain(local) -> ?SOCKET_DOMAIN_LOCAL; +enc_domain(inet) -> ?SOCKET_DOMAIN_INET; +enc_domain(inet6) -> ?SOCKET_DOMAIN_INET6; +enc_domain(Domain) -> throw({error, {invalid_domain, Domain}}). + +-spec enc_type(Domain, Type) -> non_neg_integer() when + Domain :: domain(), + Type :: type(). + +%% What combos are valid? +enc_type(_, stream) -> ?SOCKET_TYPE_STREAM; +enc_type(_, dgram) -> ?SOCKET_TYPE_DGRAM; +enc_type(_, raw) -> ?SOCKET_TYPE_RAW; +enc_type(_, seqpacket) -> ?SOCKET_TYPE_SEQPACKET; +enc_type(_, Type) -> throw({error, {invalid_type, Type}}). + +-spec enc_protocol(Type, Protocol) -> non_neg_integer() when + Type :: type(), + Protocol :: protocol(). + +enc_protocol(dgram, ip) -> ?SOCKET_PROTOCOL_IP; +enc_protocol(stream, tcp) -> ?SOCKET_PROTOCOL_TCP; +enc_protocol(dgram, udp) -> ?SOCKET_PROTOCOL_UDP; +enc_protocol(seqpacket, sctp) -> ?SOCKET_PROTOCOL_SCTP; +enc_protocol(Type, Proto) -> throw({error, {invalid_protocol, {Type, Proto}}}). + + +-spec enc_backlog(Backlog) -> non_neg_integer() when + Backlog :: non_neg_integer() | boolean(). + +enc_backlog(true) -> + ?SOCKET_LISTEN_BACKLOG_DEFAULT; +enc_backlog(false) -> + 0; +enc_backlog(Backlog) when is_integer(Backlog) andalso (Backlog >= 0) -> + Backlog. + +-spec enc_accept_flags(Flags) -> non_neg_integer() when + Flags :: accept_flags(). + +enc_accept_flags(Flags) -> + EFlags = [{nonblock, ?SOCKET_ACCEPT_FLAG_NONBLOCK}, + {cloexec, ?SOCKET_ACCEPT_FLAG_CLOEXEC}], + enc_flags(Flags, EFlags). + + +-spec enc_send_flags(Flags) -> non_neg_integer() when + Flags :: send_flags(). + +enc_send_flags(Flags) -> + EFlags = [{confirm, ?SOCKET_SEND_FLAG_CONFIRM}, + {dontroute, ?SOCKET_SEND_FLAG_DONTROUTE}, + {dontwait, ?SOCKET_SEND_FLAG_DONTWAIT}, + {eor, ?SOCKET_SEND_FLAG_EOR}, + {more, ?SOCKET_SEND_FLAG_MORE}, + {nosignal, ?SOCKET_SEND_FLAG_NOSIGNAL}, + {oob, ?SOCKET_SEND_FLAG_OOB}], + enc_flags(Flags, EFlags). + +-spec enc_recv_flags(Flags) -> non_neg_integer() when + Flags :: recv_flags(). + +enc_recv_flags(Flags) -> + EFlags = [{cmsg_cloexec, ?SOCKET_RECV_FLAG_CMSG_CLOEXEC}, + {dontwait, ?SOCKET_RECV_FLAG_DONTWAIT}, + {errqueue, ?SOCKET_RECV_FLAG_ERRQUEUE}, + {oob, ?SOCKET_RECV_FLAG_OOB}, + {peek, ?SOCKET_RECV_FLAG_PEEK}, + {trunc, ?SOCKET_RECV_FLAG_TRUNC}, + {waitall, ?SOCKET_RECV_FLAG_WAITALL}], + enc_flags(Flags, EFlags). + + +enc_flags([], _) -> + 0; +enc_flags(Flags, EFlags) -> + F = fun(Flag, Acc) -> + case lists:keysearch(Flag, 1, EFlags) of + {value, {Flag, EFlag}} -> + Acc bor (1 bsl EFlag); + false -> + throw({error, {unknown_flag, Flag}}) + end + end, + lists:foldl(F, 0, Flags). + +%% We should ...really... do something with the domain, type and protocol args... +enc_setopt_key(debug, _, _, _) -> + ?SOCKET_SETOPT_KEY_DEBUG. + +%% We should ...really... do something with the domain, type and protocol args... +enc_setopt_value(debug, V, _, _, _) when is_boolean(V) -> + V. + + +%% We should ...really... do something with the domain, type and protocol args... +enc_getopt_key(debug, _, _, _) -> + ?SOCKET_GETOPT_KEY_DEBUG. + +%% We should ...really... do something with the domain, type and protocol args... +dec_getopt_value(debug, B, _, _, _) when is_boolean(B) -> + B. + + + +%% =========================================================================== +%% +%% Misc utility functions +%% +%% =========================================================================== + +formated_timestamp() -> + format_timestamp(os:timestamp()). + +format_timestamp(Now) -> + N2T = fun(N) -> calendar:now_to_local_time(N) end, + format_timestamp(Now, N2T, true). + +format_timestamp({_N1, _N2, N3} = N, N2T, true) -> + FormatExtra = ".~.2.0w", + ArgsExtra = [N3 div 10000], + format_timestamp(N, N2T, FormatExtra, ArgsExtra); +format_timestamp({_N1, _N2, _N3} = N, N2T, false) -> + FormatExtra = "", + ArgsExtra = [], + format_timestamp(N, N2T, FormatExtra, ArgsExtra). + +format_timestamp(N, N2T, FormatExtra, ArgsExtra) -> + {Date, Time} = N2T(N), + {YYYY,MM,DD} = Date, + {Hour,Min,Sec} = Time, + FormatDate = + io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w" ++ FormatExtra, + [YYYY, MM, DD, Hour, Min, Sec] ++ ArgsExtra), + lists:flatten(FormatDate). + + +%% A timestamp in ms + +timestamp(infinity) -> + undefined; +timestamp(_) -> + timestamp(). + +timestamp() -> + {A,B,C} = os:timestamp(), + A*1000000000+B*1000+(C div 1000). + +next_timeout(infinity = Timeout, _) -> + Timeout; +next_timeout(Timeout, TS) -> + NewTimeout = Timeout - tdiff(TS, timestamp()), + if + (NewTimeout > 0) -> + NewTimeout; + true -> + 0 + end. + +tdiff(T1, T2) -> + T2 - T1. + + + + + +%% =========================================================================== +%% +%% Below follows the actual NIF-functions. +%% +%% =========================================================================== + +nif_is_loaded() -> false. + +nif_info() -> + erlang:error(badarg). + +nif_open(_Domain, _Type, _Protocol, _Extra) -> + erlang:error(badarg). + +nif_bind(_SRef, _LocalAddr) -> + erlang:error(badarg). + +nif_connect(_SRef, _Addr, _Port) -> + erlang:error(badarg). + +nif_finalize_connection(_SRef) -> + erlang:error(badarg). + +nif_listen(_SRef, _Backlog) -> + erlang:error(badarg). + +nif_accept(_SRef, _Ref) -> + erlang:error(badarg). + +nif_accept4(_SRef, _Flags, _Ref) -> + erlang:error(badarg). + +nif_send(_SRef, _Data, _Flags) -> + erlang:error(badarg). + +nif_sendto(_SRef, _Data, _Flags, _Dest, _Port) -> + erlang:error(badarg). + +nif_recv(_SRef, _Flags) -> + erlang:error(badarg). + +nif_recvfrom(_SRef, _Flags) -> + erlang:error(badarg). + +nif_cancel(_SRef, _Ref) -> + erlang:error(badarg). + +nif_close(_SRef) -> + erlang:error(badarg). + +nif_setopt(_Ref, _Key, _Val) -> + erlang:error(badarg). + +nif_getopt(_Ref, _Key) -> + erlang:error(badarg). -- cgit v1.2.3 From 9e6fda01b1af0c42cee9f983d5bddecc7eb7e240 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 9 Apr 2018 12:54:52 +0200 Subject: [socket-nif] Completed listen --- erts/preloaded/src/socket.erl | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index f8e3d0419a..0886c0d211 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -213,7 +213,10 @@ info() -> %% %% =========================================================================== +%% =========================================================================== +%% %% open - create an endpoint for communication +%% open(Domain, Type) -> open(Domain, Type, null). @@ -272,7 +275,10 @@ default_protocol(null, Type) -> throw({error, {no_default_protocol, Type}}) default_protocol(Protocol, _) -> Protocol. +%% =========================================================================== +%% %% bind - bind a name to a socket +%% -spec bind(Socket, FileOrAddr) -> ok | {error, Reason} when Socket :: socket(), @@ -318,7 +324,11 @@ bind({socket, _, SockRef}, Addr, Port) nif_bind(SockRef, {Addr, Port}). + +%% =========================================================================== +%% %% connect - initiate a connection on a socket +%% -spec connect(Socket, Addr, Port) -> ok | {error, Reason} when Socket :: socket(), @@ -364,7 +374,11 @@ connect({socket, _, SockRef}, Addr, Port, Timeout) end. + +%% =========================================================================== +%% %% listen - listen for connections on a socket +%% -spec listen(Socket, Backlog) -> ok | {error, Reason} when Socket :: socket(), @@ -375,13 +389,15 @@ listen(Socket) -> listen(Socket, ?SOCKET_LISTEN_BACKLOG_DEFAULT). listen({socket, _, SockRef}, Backlog) - when (is_integer(Backlog) andalso (Backlog >= 0)) orelse is_boolean(Backlog) -> - EBacklog = enc_backlog(Backlog), - nif_listen(SockRef, EBacklog). + when (is_integer(Backlog) andalso (Backlog >= 0)) -> + nif_listen(SockRef, Backlog). +%% =========================================================================== +%% %% accept, accept4 - accept a connection on a socket +%% -spec accept(LSocket, Timeout) -> {ok, Socket} | {error, Reason} when LSocket :: socket(), @@ -682,16 +698,6 @@ enc_protocol(seqpacket, sctp) -> ?SOCKET_PROTOCOL_SCTP; enc_protocol(Type, Proto) -> throw({error, {invalid_protocol, {Type, Proto}}}). --spec enc_backlog(Backlog) -> non_neg_integer() when - Backlog :: non_neg_integer() | boolean(). - -enc_backlog(true) -> - ?SOCKET_LISTEN_BACKLOG_DEFAULT; -enc_backlog(false) -> - 0; -enc_backlog(Backlog) when is_integer(Backlog) andalso (Backlog >= 0) -> - Backlog. - -spec enc_accept_flags(Flags) -> non_neg_integer() when Flags :: accept_flags(). -- cgit v1.2.3 From c5c8da4ecb985837817e60738811793754c679a0 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 11 Apr 2018 12:38:58 +0200 Subject: [socket-nif] Completed accept --- erts/preloaded/src/socket.erl | 96 ++++++++++++------------------------------- 1 file changed, 27 insertions(+), 69 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 0886c0d211..f3a3d493ac 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -32,7 +32,6 @@ connect/3, listen/1, listen/2, accept/1, accept/2, - accept4/1, accept4/2, accept4/3, send/2, send/3, sendto/5, recv/1, recv/2, recvfrom/1, recvfrom/2, @@ -53,6 +52,7 @@ ip_address/0, ip4_address/0, ip6_address/0, + in6_sockaddr/0, port_number/0, accept_flags/0, @@ -76,6 +76,10 @@ -type ip4_address() :: {0..255, 0..255, 0..255, 0..255}. +-type uint32() :: 0..16#FFFFFFFF. +-type ip6_flow_info() :: uint32(). +-type ip6_scope_id() :: uint32(). + -type ip6_address() :: {0..65535, 0..65535, @@ -85,6 +89,11 @@ 0..65535, 0..65535, 0..65535}. +%% We need to polish this further... +-record(in6_sockaddr, {addr :: ip6_address(), + flowinfo = 0 :: ip6_flow_info(), + scope_id :: ip6_scope_id()}). +-type in6_sockaddr() :: #in6_sockaddr{}. -type port_number() :: 0..65535. @@ -394,6 +403,7 @@ listen({socket, _, SockRef}, Backlog) + %% =========================================================================== %% %% accept, accept4 - accept a connection on a socket @@ -411,25 +421,27 @@ accept(Socket) -> %% Do we really need this optimization? accept(_, Timeout) when is_integer(Timeout) andalso (Timeout < 0) -> {error, timeout}; -accept({socket, _, LSockRef}, Timeout) +accept({socket, SI, LSockRef}, Timeout) when is_integer(Timeout) orelse (Timeout =:= infinity) -> Ref = make_ref(), - do_accept(LSockRef, Ref, Timeout). + do_accept(LSockRef, SI, Ref, Timeout). -do_accept(_, _Ref, Timeout) when is_integer(Timeout) andalso (Timeout < 0) -> +do_accept(_, _, _Ref, Timeout) when is_integer(Timeout) andalso (Timeout < 0) -> {error, timeout}; -do_accept(LSockRef, Ref, Timeout) -> +do_accept(LSockRef, SI, Ref, Timeout) -> TS = timestamp(Timeout), case nif_accept(LSockRef, Ref) of {ok, SockRef} -> - Socket = {socket, foo, SockRef}, + SocketInfo = #{domain => maps:get(domain, SI), + type => maps:get(type, SI), + protocol => maps:get(protocol, SI)}, + Socket = {socket, SocketInfo, SockRef}, {ok, Socket}; {error, eagain} -> receive {select, LSockRef, Ref, ready_input} -> - do_accept(LSockRef, make_ref(), next_timeout(TS, Timeout)) + do_accept(LSockRef, SI, make_ref(), next_timeout(TS, Timeout)) after Timeout -> - %% Shall we cancel the select? How? nif_cancel(LSockRef, Ref), flush_select_msgs(LSockRef, Ref), {error, timeout} @@ -445,56 +457,10 @@ flush_select_msgs(LSRef, Ref) -> end. --spec accept4(LSocket, Flags, Timeout) -> {ok, Socket} | {error, Reason} when - LSocket :: socket(), - Flags :: accept_flags(), - Timeout :: timeout(), - Socket :: socket(), - Reason :: term(). - -accept4(LSocket) -> - accept4(LSocket, ?SOCKET_ACCEPT_FLAGS_DEFAULT). - -accept4(LSocket, Flags) when is_list(Flags) -> - accept4(LSocket, Flags, infinity); -accept4(LSocket, Timeout) -> - accept4(LSocket, ?SOCKET_ACCEPT_FLAGS_DEFAULT, Timeout). - -%% Do we really need this optimization? -accept4(_LSocket, _Flags, Timeout) when is_integer(Timeout) andalso (Timeout < 0) -> - {error, timeout}; -accept4({socket, _, LSockRef}, Flags, Timeout) - when is_list(Flags) andalso - (is_integer(Timeout) orelse (Timeout =:= infinity)) -> - EFlags = enc_accept_flags(Flags), - Ref = make_ref(), - do_accept4(LSockRef, EFlags, Ref, Timeout). - -do_accept4(_, _EFlags, _Ref, Timeout) - when is_integer(Timeout) andalso (Timeout < 0) -> - {error, timeout}; -do_accept4(LSockRef, EFlags, Ref, Timeout) -> - TS = timestamp(Timeout), - case nif_accept4(LSockRef, EFlags, Ref) of - {ok, SockRef} -> - Socket = {socket, foo, SockRef}, - {ok, Socket}; - {error, eagain} -> - receive - {select, LSockRef, Ref, ready_input} -> - do_accept4(LSockRef, EFlags, make_ref(), - next_timeout(TS, Timeout)) - after Timeout -> - %% Shall we cancel the select? How? - nif_cancel(LSockRef, Ref), - flush_select_msgs(LSockRef, Ref), - {error, timeout} - end - end. - - - +%% =========================================================================== +%% %% send, sendto, sendmsg - send a message on a socket +%% -spec send(Socket, Data, Flags) -> ok | {error, Reason} when Socket :: socket(), @@ -511,6 +477,8 @@ send({socket, _, SockRef}, Data, Flags) nif_send(SockRef, Data, EFlags). +%% --------------------------------------------------------------------------- + -spec sendto(Socket, Data, Flags, DestAddr, Port) -> ok | {error, Reason} when Socket :: socket(), Data :: binary(), @@ -530,6 +498,8 @@ sendto({socket, _, SockRef}, Data, Flags, DestAddr, DestPort) nif_sendto(SockRef, Data, EFlags, DestAddr, DestPort). +%% --------------------------------------------------------------------------- + %% -spec sendmsg(Socket, MsgHdr, Flags) -> ok | {error, Reason} when %% Socket :: socket(), %% MsgHdr :: msg_header(), @@ -698,15 +668,6 @@ enc_protocol(seqpacket, sctp) -> ?SOCKET_PROTOCOL_SCTP; enc_protocol(Type, Proto) -> throw({error, {invalid_protocol, {Type, Proto}}}). --spec enc_accept_flags(Flags) -> non_neg_integer() when - Flags :: accept_flags(). - -enc_accept_flags(Flags) -> - EFlags = [{nonblock, ?SOCKET_ACCEPT_FLAG_NONBLOCK}, - {cloexec, ?SOCKET_ACCEPT_FLAG_CLOEXEC}], - enc_flags(Flags, EFlags). - - -spec enc_send_flags(Flags) -> non_neg_integer() when Flags :: send_flags(). @@ -856,9 +817,6 @@ nif_listen(_SRef, _Backlog) -> nif_accept(_SRef, _Ref) -> erlang:error(badarg). -nif_accept4(_SRef, _Flags, _Ref) -> - erlang:error(badarg). - nif_send(_SRef, _Data, _Flags) -> erlang:error(badarg). -- cgit v1.2.3 From 2d57ebfc6fb723a476fdcffbb366558a6fa18844 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 12 Apr 2018 18:24:00 +0200 Subject: [socket-nif] Completed send We still need to handle simultaneous ops. That is, handle if two different procs tries to send at the same time. Or a recv and send at the same time. Ops queue? --- erts/preloaded/src/socket.erl | 178 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 159 insertions(+), 19 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index f3a3d493ac..985b45a956 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -33,13 +33,21 @@ listen/1, listen/2, accept/1, accept/2, - send/2, send/3, sendto/5, - recv/1, recv/2, recvfrom/1, recvfrom/2, + send/2, send/3, send/4, + sendto/5, + %% sendmsg/4, + %% writev/4, OR SENDV? It will be strange for recv then: recvv (instead of readv) + + recv/1, recv/2, + recvfrom/1, recvfrom/2, + %% recvmsg/4, + %% readv/3, close/1, setopt/3, getopt/2, + %% ????? formated_timestamp/0 ]). @@ -457,45 +465,103 @@ flush_select_msgs(LSRef, Ref) -> end. + %% =========================================================================== %% %% send, sendto, sendmsg - send a message on a socket %% --spec send(Socket, Data, Flags) -> ok | {error, Reason} when - Socket :: socket(), - Data :: binary(), - Flags :: send_flags(), - Reason :: term(). - send(Socket, Data) -> - send(Socket, Data, ?SOCKET_SEND_FLAGS_DEFAULT). + send(Socket, Data, ?SOCKET_SEND_FLAGS_DEFAULT, infinity). -send({socket, _, SockRef}, Data, Flags) - when is_binary(Data) andalso is_list(Flags) -> +send(Socket, Data, Flags) when is_list(Flags) -> + send(Socket, Data, Flags, infinity); +send(Socket, Data, Timeout) -> + send(Socket, Data, ?SOCKET_SEND_FLAGS_DEFAULT, Timeout). + + +-spec send(Socket, Data, Flags, Timeout) -> ok | {error, Reason} when + Socket :: socket(), + Data :: iodata(), + Flags :: send_flags(), + Timeout :: timeout(), + Reason :: term(). + +send(Socket, Data, Flags, Timeout) when is_list(Data) -> + Bin = erlang:list_to_binary(Data), + send(Socket, Bin, Flags, Timeout); +send(Socket, Data, Flags, Timeout) when is_binary(Data) andalso is_list(Flags) -> EFlags = enc_send_flags(Flags), - nif_send(SockRef, Data, EFlags). + do_send(Socket, make_ref(), Data, EFlags, Timeout). + +do_send(SockRef, SendRef, Data, _EFlags, Timeout) + when (Timeout =< 0) -> + nif_cancel(SockRef, SendRef), + flush_select_msgs(SockRef, SendRef), + {error, {timeout, size(Data)}}; +do_send(SockRef, SendRef, Data, EFlags, Timeout) -> + TS = timestamp(Timeout), + case nif_send(SockRef, SendRef, Data, EFlags) of + ok -> + {ok, next_timeout(TS, Timeout)}; + {ok, Written} -> + %% We are partially done, wait for continuation + receive + {select, SockRef, SendRef, ready_output} when (Written > 0) -> + <<_:Written/binary, Rest/binary>> = Data, + do_send(SockRef, make_ref(), Rest, EFlags, + next_timeout(TS, Timeout)); + {select, SockRef, SendRef, ready_output} -> + do_send(SockRef, make_ref(), Data, EFlags, + next_timeout(TS, Timeout)) + after Timeout -> + nif_cancel(SockRef, SendRef), + flush_select_msgs(SockRef, SendRef), + {error, timeout} + end; + {error, eagain} -> + receive + {select, SockRef, SendRef, ready_output} -> + do_send(SockRef, SendRef, Data, EFlags, + next_timeout(TS, Timeout)) + after Timeout -> + nif_cancel(SockRef, SendRef), + flush_select_msgs(SockRef, SendRef), + {error, timeout} + end; + + {error, _} = ERROR -> + ERROR + end. + + %% --------------------------------------------------------------------------- +%% +%% Do we need a timeout argument here also? +%% -spec sendto(Socket, Data, Flags, DestAddr, Port) -> ok | {error, Reason} when Socket :: socket(), Data :: binary(), Flags :: send_flags(), - DestAddr :: ip_address(), + DestAddr :: null | ip_address(), Port :: port_number(), Reason :: term(). sendto({socket, _, SockRef}, Data, Flags, DestAddr, DestPort) when is_binary(Data) andalso is_list(Flags) andalso - (is_tuple(DestAddr) andalso - ((size(DestAddr) =:= 4) orelse - (size(DestAddr) =:= 8))) andalso + ((is_tuple(DestAddr) andalso + ((size(DestAddr) =:= 4) orelse + (size(DestAddr) =:= 8))) orelse + (DestAddr =:= null)) andalso (is_integer(DestPort) andalso (DestPort >= 0)) -> + %% We may need something like send/4 above? EFlags = enc_send_flags(Flags), - nif_sendto(SockRef, Data, EFlags, DestAddr, DestPort). + nif_sendto(SockRef, make_ref(), Data, EFlags, DestAddr, DestPort). + %% --------------------------------------------------------------------------- @@ -508,7 +574,73 @@ sendto({socket, _, SockRef}, Data, Flags, DestAddr, DestPort) +%% =========================================================================== +%% +%% writev - write data into multiple buffers +%% + +%% send(Socket, Data, Flags, Timeout) +%% when (is_list(Data) orelse is_binary(Data)) andalso is_list(Flags) -> +%% IOVec = erlang:iolist_to_iovec(Data), +%% EFlags = enc_send_flags(Flags), +%% send_iovec(Socket, IOVec, EFlags, Timeout). + + +%% %% Iterate over the IO-vector (list of binaries). + +%% send_iovec(_Socket, [] = _IOVec, _EFlags, _Timeout) -> +%% ok; +%% send_iovec({socket, _, SockRef} = Socket, [Bin|IOVec], EFlags, Timeout) -> +%% case do_send(SockRef, make_ref(), Bin, EFlags, Timeout) of +%% {ok, NewTimeout} -> +%% send_iovec(Socket, IOVec, EFlags, NewTimeout); +%% {error, _} = ERROR -> +%% ERROR +%% end. + + +%% do_send(SockRef, SendRef, Data, _EFlags, Timeout) +%% when (Timeout < 0) -> +%% nif_cancel(SockRef, SendRef), +%% flush_select_msgs(SockRef, SendRef), +%% {error, {timeout, size(Data)}}; +%% do_send(SockRef, SendRef, Data, EFlags, Timeout) -> +%% TS = timestamp(Timeout), +%% case nif_send(SockRef, SendRef, Data, EFlags) of +%% ok -> +%% {ok, next_timeout(TS, Timeout)}; +%% {ok, Written} -> +%% %% We are partially done, wait for continuation +%% receive +%% {select, SockRef, SendRef, ready_output} -> +%% <<_:Written/binary, Rest/binary>> = Data, +%% do_send(SockRef, make_ref(), Rest, EFlags, +%% next_timeout(TS, Timeout)) +%% after Timeout -> +%% nif_cancel(SockRef, SendRef), +%% flush_select_msgs(SockRef, SendRef), +%% {error, timeout} +%% end; +%% {error, eagain} -> +%% receive +%% {select, SockRef, SendRef, ready_output} -> +%% do_send(SockRef, SendRef, Data, EFlags, +%% next_timeout(TS, Timeout)) +%% after Timeout -> +%% nif_cancel(SockRef, SendRef), +%% flush_select_msgs(SockRef, SendRef), +%% {error, timeout} +%% end; + +%% {error, _} = ERROR -> +%% ERROR +%% end. + + +%% =========================================================================== +%% %% recv, recvfrom, recvmsg - receive a message from a socket +%% -spec recv(Socket, Flags) -> {ok, Data} | {error, Reason} when Socket :: socket(), @@ -540,6 +672,7 @@ recvfrom({socket, _, SockRef}, Flags) when is_list(Flags) -> EFlags = enc_recv_flags(Flags), nif_recvfrom(SockRef, EFlags). + %% -spec recvmsg(Socket, [out] MsgHdr, Flags) -> {ok, Data} | {error, Reason} when %% Socket :: socket(), %% MsgHdr :: msg_header(), @@ -549,6 +682,13 @@ recvfrom({socket, _, SockRef}, Flags) when is_list(Flags) -> +%% =========================================================================== +%% +%% readv - read data into multiple buffers +%% + + + %% close - close a file descriptor -spec close(Socket) -> ok | {error, Reason} when @@ -817,10 +957,10 @@ nif_listen(_SRef, _Backlog) -> nif_accept(_SRef, _Ref) -> erlang:error(badarg). -nif_send(_SRef, _Data, _Flags) -> +nif_send(_SockRef, _SendRef, _Data, _Flags) -> erlang:error(badarg). -nif_sendto(_SRef, _Data, _Flags, _Dest, _Port) -> +nif_sendto(_SRef, _SendRef, _Data, _Flags, _Dest, _Port) -> erlang:error(badarg). nif_recv(_SRef, _Flags) -> -- cgit v1.2.3 From 5920705deb70a44311e1b7552cfa73553f284164 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 13 Apr 2018 20:50:42 +0200 Subject: [socket-nif] Implemented sendto Still not handling queue'ing of multiple send requests. --- erts/preloaded/src/socket.erl | 90 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 80 insertions(+), 10 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 985b45a956..0a78feab4e 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -135,6 +135,26 @@ -type setopt_key() :: foo. -type getopt_key() :: foo. +-record(msg_hdr, + { + %% Optional address + %% On an unconnected socket this is used to specify the target + %% address for a datagram. + %% For a connected socket, this field should be specified []. + name :: list(), + + %% Scatter/gather array + iov :: [binary()], % iovec(), + + %% Ancillary (control) data + ctrl :: binary(), + + %% Unused + flags = [] :: list() + }). +-type msg_hdr() :: #msg_hdr{}. + + -define(SOCKET_DOMAIN_LOCAL, 1). -define(SOCKET_DOMAIN_UNIX, ?SOCKET_DOMAIN_LOCAL). -define(SOCKET_DOMAIN_INET, 2). @@ -542,25 +562,75 @@ do_send(SockRef, SendRef, Data, EFlags, Timeout) -> %% Do we need a timeout argument here also? %% --spec sendto(Socket, Data, Flags, DestAddr, Port) -> ok | {error, Reason} when +sendto(Socket, Data, Flags, DestAddr, DestPort) -> + sendto(Socket, Data, Flags, DestAddr, DestPort, infinity). + +-spec sendto(Socket, Data, Flags, DestAddr, DestPort, Timeout) -> + ok | {error, Reason} when Socket :: socket(), Data :: binary(), Flags :: send_flags(), DestAddr :: null | ip_address(), - Port :: port_number(), + DestPort :: port_number(), + Timeout :: timeout(), Reason :: term(). -sendto({socket, _, SockRef}, Data, Flags, DestAddr, DestPort) +sendto(Socket, Data, Flags, DestAddr, DestPort, Timeout) when is_list(Data) -> + Bin = erlang:list_to_binary(Data), + sendto(Socket, Bin, Flags, DestAddr, DestPort, Timeout); +sendto(Socket, Data, Flags, DestAddr, DestPort, Timeout) when is_binary(Data) andalso is_list(Flags) andalso - ((is_tuple(DestAddr) andalso - ((size(DestAddr) =:= 4) orelse - (size(DestAddr) =:= 8))) orelse - (DestAddr =:= null)) andalso - (is_integer(DestPort) andalso (DestPort >= 0)) -> - %% We may need something like send/4 above? + (is_tuple(DestAddr) orelse (DestAddr =:= null)) andalso + is_integer(DestPort) andalso + (is_integer(Timeout) orelse (Timeout =:= infinity)) -> EFlags = enc_send_flags(Flags), - nif_sendto(SockRef, make_ref(), Data, EFlags, DestAddr, DestPort). + do_sendto(Socket, make_ref(), Data, EFlags, DestAddr, DestPort, Timeout). + +do_sendto(SockRef, SendRef, Data, _EFlags, _DestAddr, _DestPort, Timeout) + when (Timeout =< 0) -> + nif_cancel(SockRef, SendRef), + flush_select_msgs(SockRef, SendRef), + {error, {timeout, size(Data)}}; +do_sendto(SockRef, SendRef, Data, EFlags, DestAddr, DestPort, Timeout) -> + TS = timestamp(Timeout), + case nif_sendto(SockRef, SendRef, Data, DestAddr, DestPort, EFlags) of + ok -> + {ok, next_timeout(TS, Timeout)}; + {ok, Written} -> + %% We are partially done, wait for continuation + receive + {select, SockRef, SendRef, ready_output} when (Written > 0) -> + <<_:Written/binary, Rest/binary>> = Data, + do_sendto(SockRef, make_ref(), Rest, EFlags, + DestAddr, DestPort, + next_timeout(TS, Timeout)); + {select, SockRef, SendRef, ready_output} -> + do_sendto(SockRef, make_ref(), Data, EFlags, + DestAddr, DestPort, + next_timeout(TS, Timeout)) + after Timeout -> + nif_cancel(SockRef, SendRef), + flush_select_msgs(SockRef, SendRef), + {error, timeout} + end; + {error, eagain} -> + %% Is this what we can expect? + %% If we have to wait because there is another ongoing write?? + receive + {select, SockRef, SendRef, ready_output} -> + do_sendto(SockRef, SendRef, Data, EFlags, + DestAddr, DestPort, + next_timeout(TS, Timeout)) + after Timeout -> + nif_cancel(SockRef, SendRef), + flush_select_msgs(SockRef, SendRef), + {error, timeout} + end; + + {error, _} = ERROR -> + ERROR + end. -- cgit v1.2.3 From 28611d6e6daab8ae24e5e593c001bcd6442506eb Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 19 Apr 2018 10:57:54 +0200 Subject: [socket-nif] Completed recv Need to fix the use of the request ref (ID) handling in previous functions. --- erts/preloaded/src/socket.erl | 204 +++++++++++++++++++++++++++++++++--------- 1 file changed, 164 insertions(+), 40 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 0a78feab4e..6784477123 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -38,7 +38,7 @@ %% sendmsg/4, %% writev/4, OR SENDV? It will be strange for recv then: recvv (instead of readv) - recv/1, recv/2, + recv/2, recv/3, recv/4, recvfrom/1, recvfrom/2, %% recvmsg/4, %% readv/3, @@ -173,11 +173,7 @@ -define(SOCKET_LISTEN_BACKLOG_DEFAULT, 5). -%% Bit numbers (from right). --define(SOCKET_ACCEPT_FLAG_NONBLOCK, 0). --define(SOCKET_ACCEPT_FLAG_CLOEXEC, 1). - --define(SOCKET_ACCEPT_FLAGS_DEFAULT, []). +-define(SOCKET_ACCEPT_TIMEOUT_DEFAULT, infinity). -define(SOCKET_SEND_FLAG_CONFIRM, 0). -define(SOCKET_SEND_FLAG_DONTROUTE, 1). @@ -187,7 +183,9 @@ -define(SOCKET_SEND_FLAG_NOSIGNAL, 5). -define(SOCKET_SEND_FLAG_OOB, 6). --define(SOCKET_SEND_FLAGS_DEFAULT, []). +-define(SOCKET_SEND_FLAGS_DEFAULT, []). +-define(SOCKET_SEND_TIMEOUT_DEFAULT, infinity). +-define(SOCKET_SENDTO_TIMEOUT_DEFAULT, ?SOCKET_SEND_TIMEOUT_DEFAULT). -define(SOCKET_RECV_FLAG_CMSG_CLOEXEC, 0). -define(SOCKET_RECV_FLAG_DONTWAIT, 1). @@ -197,7 +195,8 @@ -define(SOCKET_RECV_FLAG_TRUNC, 5). -define(SOCKET_RECV_FLAG_WAITALL, 6). --define(SOCKET_RECV_FLAGS_DEFAULT, []). +-define(SOCKET_RECV_FLAGS_DEFAULT, []). +-define(SOCKET_RECV_TIMEOUT_DEFAULT, infinity). -define(SOCKET_SETOPT_KEY_DEBUG, 0). @@ -403,7 +402,7 @@ connect({socket, _, SockRef}, Addr, Port, Timeout) {select, SockRef, Ref, ready_output} -> nif_finalize_connection(SockRef) after NewTimeout -> - nif_cancel(SockRef, Ref), + nif_cancel(SockRef, connect, Ref), {error, timeout} end; {error, _} = ERROR -> @@ -444,7 +443,7 @@ listen({socket, _, SockRef}, Backlog) Reason :: term(). accept(Socket) -> - accept(Socket, infinity). + accept(Socket, ?SOCKET_ACCEPT_TIMEOUT_DEFAULT). %% Do we really need this optimization? accept(_, Timeout) when is_integer(Timeout) andalso (Timeout < 0) -> @@ -466,11 +465,12 @@ do_accept(LSockRef, SI, Ref, Timeout) -> Socket = {socket, SocketInfo, SockRef}, {ok, Socket}; {error, eagain} -> + NewTimeout = next_timeout(TS, Timeout), receive {select, LSockRef, Ref, ready_input} -> do_accept(LSockRef, SI, make_ref(), next_timeout(TS, Timeout)) - after Timeout -> - nif_cancel(LSockRef, Ref), + after NewTimeout -> + nif_cancel(LSockRef, accept, Ref), flush_select_msgs(LSockRef, Ref), {error, timeout} end @@ -492,14 +492,13 @@ flush_select_msgs(LSRef, Ref) -> %% send(Socket, Data) -> - send(Socket, Data, ?SOCKET_SEND_FLAGS_DEFAULT, infinity). + send(Socket, Data, ?SOCKET_SEND_FLAGS_DEFAULT, ?SOCKET_SEND_TIMEOUT_DEFAULT). send(Socket, Data, Flags) when is_list(Flags) -> - send(Socket, Data, Flags, infinity); + send(Socket, Data, Flags, ?SOCKET_SEND_TIMEOUT_DEFAULT); send(Socket, Data, Timeout) -> send(Socket, Data, ?SOCKET_SEND_FLAGS_DEFAULT, Timeout). - -spec send(Socket, Data, Flags, Timeout) -> ok | {error, Reason} when Socket :: socket(), Data :: iodata(), @@ -516,15 +515,20 @@ send(Socket, Data, Flags, Timeout) when is_binary(Data) andalso is_list(Flags) do_send(SockRef, SendRef, Data, _EFlags, Timeout) when (Timeout =< 0) -> - nif_cancel(SockRef, SendRef), + %% + %% THIS IS THE WRONG SEND REF + %% WE SHOULD NOT HAVE THIS REF AS AN ARGUMENT - SEE RECV + %% + nif_cancel(SockRef, send, SendRef), flush_select_msgs(SockRef, SendRef), {error, {timeout, size(Data)}}; do_send(SockRef, SendRef, Data, EFlags, Timeout) -> TS = timestamp(Timeout), case nif_send(SockRef, SendRef, Data, EFlags) of ok -> - {ok, next_timeout(TS, Timeout)}; + ok; {ok, Written} -> + NewTimeout = next_timeout(TS, Timeout), %% We are partially done, wait for continuation receive {select, SockRef, SendRef, ready_output} when (Written > 0) -> @@ -534,8 +538,8 @@ do_send(SockRef, SendRef, Data, EFlags, Timeout) -> {select, SockRef, SendRef, ready_output} -> do_send(SockRef, make_ref(), Data, EFlags, next_timeout(TS, Timeout)) - after Timeout -> - nif_cancel(SockRef, SendRef), + after NewTimeout -> + nif_cancel(SockRef, send, SendRef), flush_select_msgs(SockRef, SendRef), {error, timeout} end; @@ -545,7 +549,7 @@ do_send(SockRef, SendRef, Data, EFlags, Timeout) -> do_send(SockRef, SendRef, Data, EFlags, next_timeout(TS, Timeout)) after Timeout -> - nif_cancel(SockRef, SendRef), + nif_cancel(SockRef, send, SendRef), flush_select_msgs(SockRef, SendRef), {error, timeout} end; @@ -563,7 +567,7 @@ do_send(SockRef, SendRef, Data, EFlags, Timeout) -> %% sendto(Socket, Data, Flags, DestAddr, DestPort) -> - sendto(Socket, Data, Flags, DestAddr, DestPort, infinity). + sendto(Socket, Data, Flags, DestAddr, DestPort, ?SOCKET_SENDTO_TIMEOUT_DEFAULT). -spec sendto(Socket, Data, Flags, DestAddr, DestPort, Timeout) -> ok | {error, Reason} when @@ -589,12 +593,16 @@ sendto(Socket, Data, Flags, DestAddr, DestPort, Timeout) do_sendto(SockRef, SendRef, Data, _EFlags, _DestAddr, _DestPort, Timeout) when (Timeout =< 0) -> - nif_cancel(SockRef, SendRef), + %% + %% THIS IS THE WRONG SEND REF + %% WE SHOULD NOT HAVE THIS REF AS AN ARGUMENT - SEE RECV + %% + nif_cancel(SockRef, sendto, SendRef), flush_select_msgs(SockRef, SendRef), {error, {timeout, size(Data)}}; do_sendto(SockRef, SendRef, Data, EFlags, DestAddr, DestPort, Timeout) -> TS = timestamp(Timeout), - case nif_sendto(SockRef, SendRef, Data, DestAddr, DestPort, EFlags) of + case nif_sendto(SockRef, SendRef, Data, EFlags, DestAddr, DestPort) of ok -> {ok, next_timeout(TS, Timeout)}; {ok, Written} -> @@ -610,7 +618,7 @@ do_sendto(SockRef, SendRef, Data, EFlags, DestAddr, DestPort, Timeout) -> DestAddr, DestPort, next_timeout(TS, Timeout)) after Timeout -> - nif_cancel(SockRef, SendRef), + nif_cancel(SockRef, sendto, SendRef), flush_select_msgs(SockRef, SendRef), {error, timeout} end; @@ -623,7 +631,7 @@ do_sendto(SockRef, SendRef, Data, EFlags, DestAddr, DestPort, Timeout) -> DestAddr, DestPort, next_timeout(TS, Timeout)) after Timeout -> - nif_cancel(SockRef, SendRef), + nif_cancel(SockRef, sendto, SendRef), flush_select_msgs(SockRef, SendRef), {error, timeout} end; @@ -711,20 +719,136 @@ do_sendto(SockRef, SendRef, Data, EFlags, DestAddr, DestPort, Timeout) -> %% %% recv, recvfrom, recvmsg - receive a message from a socket %% +%% Description: +%% There is a special case for the argument Length. If its set to zero (0), +%% it means "give me everything you have". +%% +%% Returns: {ok, Binary} | {error, Reason} +%% Binary - The received data as a binary +%% Reason - The error reason: +%% timeout | {timeout, AccData} | +%% posix() | {posix(), AccData} | +%% atom() | {atom(), AccData} +%% AccData - The data (as a binary) that we did manage to receive +%% before the timeout. +%% +%% Arguments: +%% Socket - The socket to read from. +%% Length - The number of bytes to read. +%% Flags - A list of "options" for the read. +%% Timeout - Time-out in milliseconds. + +recv(Socket, Length) -> + recv(Socket, Length, + ?SOCKET_RECV_FLAGS_DEFAULT, + ?SOCKET_RECV_TIMEOUT_DEFAULT). + +recv(Socket, Length, Flags) when is_list(Flags) -> + recv(Socket, Length, Flags, ?SOCKET_RECV_TIMEOUT_DEFAULT); +recv(Socket, Length, Timeout) -> + recv(Socket, Length, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout). + +-spec recv(Socket, Length, Flags, Timeout) -> {ok, Data} | {error, Reason} when + Socket :: socket(), + Length :: non_neg_integer(), + Flags :: recv_flags(), + Timeout :: timeout(), + Data :: binary(), + Reason :: term(). --spec recv(Socket, Flags) -> {ok, Data} | {error, Reason} when - Socket :: socket(), - Flags :: recv_flags(), - Data :: binary(), - Reason :: term(). +recv(Socket, Length, Flags, Timeout) + when (is_integer(Length) andalso (Length >= 0)) andalso + is_list(Flags) andalso + (is_integer(Timeout) orelse (Timeout =:= infinity)) -> + EFlags = enc_recv_flags(Flags), + do_recv(Socket, Length, EFlags, <<>>, EFlags). + +do_recv({socket, _, SockRef} = Socket, Length, EFlags, Acc, Timeout) + when (Timeout =:= infinity) orelse + (is_integer(Timeout) andalso (Timeout > 0)) -> + TS = timestamp(Timeout), + RecvRef = make_ref(), + case nif_recv(SockRef, RecvRef, Length, EFlags) of + {ok, true = _Complete, Bin} when (size(Acc) =:= 0) -> + {ok, Bin}; + {ok, true = _Complete, Bin} -> + {ok, <>}; + + %% It depends on the amount of bytes we tried to read: + %% 0 - Read everything available + %% We got something, but there may be more - keep reading. + %% > 0 - We got a part of the message and we will be notified + %% when there is more to read (a select message) + {ok, false = _Complete, Bin} when (Length =:= 0) -> + do_recv(Socket, Length, EFlags, + <>, + next_timeout(TS, Timeout)); + + {ok, false = _Completed, Bin} when (size(Acc) =:= 0) -> + %% We got the first chunk of it. + %% We will be notified (select message) when there + %% is more to read. + NewTimeout = next_timeout(TS, Timeout), + receive + {select, SockRef, RecvRef, ready_input} -> + do_recv(Socket, Length-size(Bin), EFlags, + Bin, + next_timeout(TS, Timeout)) + after NewTimeout -> + nif_cancel(SockRef, recv, RecvRef), + flush_select_msgs(SockRef, RecvRef), + {error, {timeout, Acc}} + end; + + {ok, false = _Completed, Bin} -> + %% We got a chunk of it! + NewTimeout = next_timeout(TS, Timeout), + receive + {select, SockRef, RecvRef, ready_input} -> + do_recv(Socket, Length-size(Bin), EFlags, + <>, + next_timeout(TS, Timeout)) + after NewTimeout -> + nif_cancel(SockRef, recv, RecvRef), + flush_select_msgs(SockRef, RecvRef), + {error, {timeout, Acc}} + end; -recv(Socket) -> - recv(Socket, ?SOCKET_RECV_FLAGS_DEFAULT). + %% We return with the accumulated binary regardless if its empty... + {error, eagain} when (Length =:= 0) -> + {ok, Acc}; -%% WE "may" need a timeout option here... -recv({socket, _, SockRef}, Flags) when is_list(Flags) -> - EFlags = enc_recv_flags(Flags), - nif_recv(SockRef, EFlags). + {error, eagain} -> + %% There is nothing just now, but we will be notified when there + %% is something to read (a select message). + NewTimeout = next_timeout(TS, Timeout), + receive + {select, SockRef, RecvRef, ready_input} -> + do_recv(Socket, Length, EFlags, + Acc, + next_timeout(TS, Timeout)) + after NewTimeout -> + nif_cancel(SockRef, recv, RecvRef), + flush_select_msgs(SockRef, RecvRef), + {error, timeout} + end; + + {error, _} = ERROR when (size(Acc) =:= 0) -> + ERROR; + + {error, Reason} -> + {error, {Reason, Acc}} + + end; + +do_recv({socket, _, SockRef} = _Socket, 0 = _Length, _Eflags, Acc, _Timeout) -> + %% The current recv operation is to be cancelled, so no need for a ref... + %% The cancel will end our 'read everything you have' and "activate" + %% any waiting readers. + nif_cancel(SockRef, recv, undefined), + {ok, Acc}; +do_recv(_Socket, _Length, _EFlags, Acc, _Timeout) -> + {error, {timeout, Acc}}. -spec recvfrom(Socket, Flags) -> {ok, Data, SrcAddr, SrcPort} | {error, Reason} when @@ -980,9 +1104,9 @@ timestamp() -> {A,B,C} = os:timestamp(), A*1000000000+B*1000+(C div 1000). -next_timeout(infinity = Timeout, _) -> +next_timeout(_, infinity = Timeout) -> Timeout; -next_timeout(Timeout, TS) -> +next_timeout(TS, Timeout) -> NewTimeout = Timeout - tdiff(TS, timestamp()), if (NewTimeout > 0) -> @@ -1033,13 +1157,13 @@ nif_send(_SockRef, _SendRef, _Data, _Flags) -> nif_sendto(_SRef, _SendRef, _Data, _Flags, _Dest, _Port) -> erlang:error(badarg). -nif_recv(_SRef, _Flags) -> +nif_recv(_SRef, _RecvRef, _Length, _Flags) -> erlang:error(badarg). nif_recvfrom(_SRef, _Flags) -> erlang:error(badarg). -nif_cancel(_SRef, _Ref) -> +nif_cancel(_SRef, _Op, _Ref) -> erlang:error(badarg). nif_close(_SRef) -> -- cgit v1.2.3 From d5aecb115070de76cb42b44edee6bbcb5f4a3724 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 19 Apr 2018 11:45:00 +0200 Subject: [socket-nif] Corrected timout handling The timeout handling for accept and send was unnecessarily complex. --- erts/preloaded/src/socket.erl | 151 +++++++++++++++++++++--------------------- 1 file changed, 75 insertions(+), 76 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 6784477123..0dadcecaa0 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -446,18 +446,16 @@ accept(Socket) -> accept(Socket, ?SOCKET_ACCEPT_TIMEOUT_DEFAULT). %% Do we really need this optimization? -accept(_, Timeout) when is_integer(Timeout) andalso (Timeout < 0) -> +accept(_, Timeout) when is_integer(Timeout) andalso (Timeout =< 0) -> {error, timeout}; accept({socket, SI, LSockRef}, Timeout) when is_integer(Timeout) orelse (Timeout =:= infinity) -> - Ref = make_ref(), - do_accept(LSockRef, SI, Ref, Timeout). + do_accept(LSockRef, SI, Timeout). -do_accept(_, _, _Ref, Timeout) when is_integer(Timeout) andalso (Timeout < 0) -> - {error, timeout}; -do_accept(LSockRef, SI, Ref, Timeout) -> - TS = timestamp(Timeout), - case nif_accept(LSockRef, Ref) of +do_accept(LSockRef, SI, Timeout) -> + TS = timestamp(Timeout), + AccRef = make_ref(), + case nif_accept(LSockRef, AccRef) of {ok, SockRef} -> SocketInfo = #{domain => maps:get(domain, SI), type => maps:get(type, SI), @@ -467,23 +465,15 @@ do_accept(LSockRef, SI, Ref, Timeout) -> {error, eagain} -> NewTimeout = next_timeout(TS, Timeout), receive - {select, LSockRef, Ref, ready_input} -> - do_accept(LSockRef, SI, make_ref(), next_timeout(TS, Timeout)) + {select, LSockRef, AccRef, ready_input} -> + do_accept(LSockRef, SI, next_timeout(TS, Timeout)) after NewTimeout -> - nif_cancel(LSockRef, accept, Ref), - flush_select_msgs(LSockRef, Ref), + nif_cancel(LSockRef, accept, AccRef), + flush_select_msgs(LSockRef, AccRef), {error, timeout} end end. -flush_select_msgs(LSRef, Ref) -> - receive - {select, LSRef, Ref, _} -> - flush_select_msgs(LSRef, Ref) - after 0 -> - ok - end. - %% =========================================================================== @@ -511,19 +501,11 @@ send(Socket, Data, Flags, Timeout) when is_list(Data) -> send(Socket, Bin, Flags, Timeout); send(Socket, Data, Flags, Timeout) when is_binary(Data) andalso is_list(Flags) -> EFlags = enc_send_flags(Flags), - do_send(Socket, make_ref(), Data, EFlags, Timeout). - -do_send(SockRef, SendRef, Data, _EFlags, Timeout) - when (Timeout =< 0) -> - %% - %% THIS IS THE WRONG SEND REF - %% WE SHOULD NOT HAVE THIS REF AS AN ARGUMENT - SEE RECV - %% - nif_cancel(SockRef, send, SendRef), - flush_select_msgs(SockRef, SendRef), - {error, {timeout, size(Data)}}; -do_send(SockRef, SendRef, Data, EFlags, Timeout) -> - TS = timestamp(Timeout), + do_send(Socket, Data, EFlags, Timeout). + +do_send(SockRef, Data, EFlags, Timeout) -> + TS = timestamp(Timeout), + SendRef = make_ref(), case nif_send(SockRef, SendRef, Data, EFlags) of ok -> ok; @@ -533,25 +515,25 @@ do_send(SockRef, SendRef, Data, EFlags, Timeout) -> receive {select, SockRef, SendRef, ready_output} when (Written > 0) -> <<_:Written/binary, Rest/binary>> = Data, - do_send(SockRef, make_ref(), Rest, EFlags, + do_send(SockRef, Rest, EFlags, next_timeout(TS, Timeout)); {select, SockRef, SendRef, ready_output} -> - do_send(SockRef, make_ref(), Data, EFlags, + do_send(SockRef, Data, EFlags, next_timeout(TS, Timeout)) after NewTimeout -> nif_cancel(SockRef, send, SendRef), flush_select_msgs(SockRef, SendRef), - {error, timeout} + {error, {timeout, size(Data)}} end; {error, eagain} -> receive {select, SockRef, SendRef, ready_output} -> - do_send(SockRef, SendRef, Data, EFlags, + do_send(SockRef, Data, EFlags, next_timeout(TS, Timeout)) after Timeout -> nif_cancel(SockRef, send, SendRef), flush_select_msgs(SockRef, SendRef), - {error, timeout} + {error, {timeout, size(Data)}} end; {error, _} = ERROR -> @@ -563,8 +545,6 @@ do_send(SockRef, SendRef, Data, EFlags, Timeout) -> %% --------------------------------------------------------------------------- %% -%% Do we need a timeout argument here also? -%% sendto(Socket, Data, Flags, DestAddr, DestPort) -> sendto(Socket, Data, Flags, DestAddr, DestPort, ?SOCKET_SENDTO_TIMEOUT_DEFAULT). @@ -589,46 +569,36 @@ sendto(Socket, Data, Flags, DestAddr, DestPort, Timeout) is_integer(DestPort) andalso (is_integer(Timeout) orelse (Timeout =:= infinity)) -> EFlags = enc_send_flags(Flags), - do_sendto(Socket, make_ref(), Data, EFlags, DestAddr, DestPort, Timeout). - -do_sendto(SockRef, SendRef, Data, _EFlags, _DestAddr, _DestPort, Timeout) - when (Timeout =< 0) -> - %% - %% THIS IS THE WRONG SEND REF - %% WE SHOULD NOT HAVE THIS REF AS AN ARGUMENT - SEE RECV - %% - nif_cancel(SockRef, sendto, SendRef), - flush_select_msgs(SockRef, SendRef), - {error, {timeout, size(Data)}}; -do_sendto(SockRef, SendRef, Data, EFlags, DestAddr, DestPort, Timeout) -> - TS = timestamp(Timeout), + do_sendto(Socket, Data, EFlags, DestAddr, DestPort, Timeout). + +do_sendto(SockRef, Data, EFlags, DestAddr, DestPort, Timeout) -> + TS = timestamp(Timeout), + SendRef = make_ref(), case nif_sendto(SockRef, SendRef, Data, EFlags, DestAddr, DestPort) of ok -> - {ok, next_timeout(TS, Timeout)}; + %% We are done + ok; + {ok, Written} -> %% We are partially done, wait for continuation receive {select, SockRef, SendRef, ready_output} when (Written > 0) -> <<_:Written/binary, Rest/binary>> = Data, - do_sendto(SockRef, make_ref(), Rest, EFlags, - DestAddr, DestPort, + do_sendto(SockRef, Rest, EFlags, DestAddr, DestPort, next_timeout(TS, Timeout)); {select, SockRef, SendRef, ready_output} -> - do_sendto(SockRef, make_ref(), Data, EFlags, - DestAddr, DestPort, + do_sendto(SockRef, Data, EFlags, DestAddr, DestPort, next_timeout(TS, Timeout)) after Timeout -> nif_cancel(SockRef, sendto, SendRef), flush_select_msgs(SockRef, SendRef), {error, timeout} end; + {error, eagain} -> - %% Is this what we can expect? - %% If we have to wait because there is another ongoing write?? receive {select, SockRef, SendRef, ready_output} -> - do_sendto(SockRef, SendRef, Data, EFlags, - DestAddr, DestPort, + do_sendto(SockRef, Data, EFlags, DestAddr, DestPort, next_timeout(TS, Timeout)) after Timeout -> nif_cancel(SockRef, sendto, SendRef), @@ -761,9 +731,13 @@ recv(Socket, Length, Flags, Timeout) is_list(Flags) andalso (is_integer(Timeout) orelse (Timeout =:= infinity)) -> EFlags = enc_recv_flags(Flags), - do_recv(Socket, Length, EFlags, <<>>, EFlags). + do_recv(Socket, undefined, Length, EFlags, <<>>, EFlags). -do_recv({socket, _, SockRef} = Socket, Length, EFlags, Acc, Timeout) +%% We need to pass the "old recv ref" around because of the special case +%% with Length = 0. This case makes it neccessary to have a timeout function +%% clause since we may never wait for anything (no receive select), and so the +%% the only timeout check will be the function clause. +do_recv({socket, _, SockRef} = Socket, _OldRef, Length, EFlags, Acc, Timeout) when (Timeout =:= infinity) orelse (is_integer(Timeout) andalso (Timeout > 0)) -> TS = timestamp(Timeout), @@ -780,7 +754,8 @@ do_recv({socket, _, SockRef} = Socket, Length, EFlags, Acc, Timeout) %% > 0 - We got a part of the message and we will be notified %% when there is more to read (a select message) {ok, false = _Complete, Bin} when (Length =:= 0) -> - do_recv(Socket, Length, EFlags, + do_recv(Socket, RecvRef, + Length, EFlags, <>, next_timeout(TS, Timeout)); @@ -791,7 +766,8 @@ do_recv({socket, _, SockRef} = Socket, Length, EFlags, Acc, Timeout) NewTimeout = next_timeout(TS, Timeout), receive {select, SockRef, RecvRef, ready_input} -> - do_recv(Socket, Length-size(Bin), EFlags, + do_recv(Socket, RecvRef, + Length-size(Bin), EFlags, Bin, next_timeout(TS, Timeout)) after NewTimeout -> @@ -805,7 +781,8 @@ do_recv({socket, _, SockRef} = Socket, Length, EFlags, Acc, Timeout) NewTimeout = next_timeout(TS, Timeout), receive {select, SockRef, RecvRef, ready_input} -> - do_recv(Socket, Length-size(Bin), EFlags, + do_recv(Socket, RecvRef, + Length-size(Bin), EFlags, <>, next_timeout(TS, Timeout)) after NewTimeout -> @@ -824,7 +801,8 @@ do_recv({socket, _, SockRef} = Socket, Length, EFlags, Acc, Timeout) NewTimeout = next_timeout(TS, Timeout), receive {select, SockRef, RecvRef, ready_input} -> - do_recv(Socket, Length, EFlags, + do_recv(Socket, RecvRef, + Length, EFlags, Acc, next_timeout(TS, Timeout)) after NewTimeout -> @@ -841,16 +819,26 @@ do_recv({socket, _, SockRef} = Socket, Length, EFlags, Acc, Timeout) end; -do_recv({socket, _, SockRef} = _Socket, 0 = _Length, _Eflags, Acc, _Timeout) -> +do_recv({socket, _, SockRef} = _Socket, RecvRef, + 0 = _Length, _Eflags, Acc, _Timeout) -> %% The current recv operation is to be cancelled, so no need for a ref... %% The cancel will end our 'read everything you have' and "activate" - %% any waiting readers. - nif_cancel(SockRef, recv, undefined), + %% any waiting reader. + nif_cancel(SockRef, recv, RecvRef), {ok, Acc}; -do_recv(_Socket, _Length, _EFlags, Acc, _Timeout) -> - {error, {timeout, Acc}}. +do_recv(_Socket, _RecvRef, _Length, _EFlags, Acc, _Timeout) when (size(Acc) > 0) -> + {error, {timeout, Acc}}; +do_recv(_Socket, _RecvRef, _Length, _EFlags, _Acc, _Timeout) -> + {error, timeout}. + +%% --------------------------------------------------------------------------- +%% + +recvfrom(Socket) -> + recvfrom(Socket, ?SOCKET_RECV_FLAGS_DEFAULT). + -spec recvfrom(Socket, Flags) -> {ok, Data, SrcAddr, SrcPort} | {error, Reason} when Socket :: socket(), Flags :: recv_flags(), @@ -859,14 +847,14 @@ do_recv(_Socket, _Length, _EFlags, Acc, _Timeout) -> SrcPort :: port_number(), Reason :: term(). -recvfrom(Socket) -> - recvfrom(Socket, ?SOCKET_RECV_FLAGS_DEFAULT). - recvfrom({socket, _, SockRef}, Flags) when is_list(Flags) -> EFlags = enc_recv_flags(Flags), nif_recvfrom(SockRef, EFlags). +%% --------------------------------------------------------------------------- +%% + %% -spec recvmsg(Socket, [out] MsgHdr, Flags) -> {ok, Data} | {error, Reason} when %% Socket :: socket(), %% MsgHdr :: msg_header(), @@ -883,6 +871,8 @@ recvfrom({socket, _, SockRef}, Flags) when is_list(Flags) -> +%% =========================================================================== +%% %% close - close a file descriptor -spec close(Socket) -> ok | {error, Reason} when @@ -1067,6 +1057,15 @@ dec_getopt_value(debug, B, _, _, _) when is_boolean(B) -> %% %% =========================================================================== +flush_select_msgs(LSRef, Ref) -> + receive + {select, LSRef, Ref, _} -> + flush_select_msgs(LSRef, Ref) + after 0 -> + ok + end. + + formated_timestamp() -> format_timestamp(os:timestamp()). -- cgit v1.2.3 From 04335ca6aedfc5ad9f0d6a8d193dfd76a222291c Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 23 Apr 2018 10:40:28 +0200 Subject: [socket-nif] Completed the recv and recvfrom functions Also updated the socket type (now a record for easy use). --- erts/preloaded/src/socket.erl | 172 ++++++++++++++++++++++++++++-------------- 1 file changed, 115 insertions(+), 57 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 0dadcecaa0..bae561cd51 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -39,7 +39,7 @@ %% writev/4, OR SENDV? It will be strange for recv then: recvv (instead of readv) recv/2, recv/3, recv/4, - recvfrom/1, recvfrom/2, + recvfrom/1, recvfrom/2, recvfrom/3, recvfrom/4, %% recvmsg/4, %% readv/3, @@ -106,10 +106,10 @@ -type port_number() :: 0..65535. -type socket_info() :: map(). -%% -record(socket, {info :: socket_info, -%% ref :: reference()}). --opaque socket() :: {socket, socket_info(), reference()}. -%% -opaque socket() :: #socket{}. +-record(socket, {info :: socket_info(), + ref :: reference()}). +%% -opaque socket() :: {socket, socket_info(), reference()}. +-opaque socket() :: #socket{}. -type accept_flags() :: [accept_flag()]. -type accept_flag() :: nonblock | cloexec. @@ -117,20 +117,26 @@ -type send_flags() :: [send_flag()]. -type send_flag() :: confirm | dontroute | - dontwait | eor | more | nosignal | oob. +%% Extend with OWN flags for other usage: +%% - adapt-buffer-sz: +%% This will have the effect that the nif recvfrom will use +%% MSG_PEEK to ensure no part of the message is lost, but if +%% necessary adapt (increase) the buffer size until all of +%% it fits. +%% +%% Note that not all of these flags is useful for every recv function! +%% -type recv_flags() :: [recv_flag()]. -type recv_flag() :: cmsg_cloexec | - dontwait | errqueue | oob | peek | - trunc | - waitall. + trunc. -type setopt_key() :: foo. -type getopt_key() :: foo. @@ -177,23 +183,20 @@ -define(SOCKET_SEND_FLAG_CONFIRM, 0). -define(SOCKET_SEND_FLAG_DONTROUTE, 1). --define(SOCKET_SEND_FLAG_DONTWAIT, 2). --define(SOCKET_SEND_FLAG_EOR, 3). --define(SOCKET_SEND_FLAG_MORE, 4). --define(SOCKET_SEND_FLAG_NOSIGNAL, 5). --define(SOCKET_SEND_FLAG_OOB, 6). +-define(SOCKET_SEND_FLAG_EOR, 2). +-define(SOCKET_SEND_FLAG_MORE, 3). +-define(SOCKET_SEND_FLAG_NOSIGNAL, 4). +-define(SOCKET_SEND_FLAG_OOB, 5). -define(SOCKET_SEND_FLAGS_DEFAULT, []). -define(SOCKET_SEND_TIMEOUT_DEFAULT, infinity). -define(SOCKET_SENDTO_TIMEOUT_DEFAULT, ?SOCKET_SEND_TIMEOUT_DEFAULT). -define(SOCKET_RECV_FLAG_CMSG_CLOEXEC, 0). --define(SOCKET_RECV_FLAG_DONTWAIT, 1). --define(SOCKET_RECV_FLAG_ERRQUEUE, 2). --define(SOCKET_RECV_FLAG_OOB, 3). --define(SOCKET_RECV_FLAG_PEEK, 4). --define(SOCKET_RECV_FLAG_TRUNC, 5). --define(SOCKET_RECV_FLAG_WAITALL, 6). +-define(SOCKET_RECV_FLAG_ERRQUEUE, 1). +-define(SOCKET_RECV_FLAG_OOB, 2). +-define(SOCKET_RECV_FLAG_PEEK, 3). +-define(SOCKET_RECV_FLAG_TRUNC, 4). -define(SOCKET_RECV_FLAGS_DEFAULT, []). -define(SOCKET_RECV_TIMEOUT_DEFAULT, infinity). @@ -287,7 +290,8 @@ open(Domain, Type, Protocol0, Extra) when is_map(Extra) -> SocketInfo = #{domain => Domain, type => Type, protocol => Protocol}, - Socket = {socket, SocketInfo, SockRef}, + Socket = #socket{info = SocketInfo, + ref = SockRef}, {ok, Socket}; {error, _} = ERROR -> ERROR @@ -352,7 +356,7 @@ bind(Socket, Addr) when is_tuple(Addr) orelse Reason :: term(). %% Shall we keep info about domain so that we can verify address? -bind({socket, _, SockRef}, Addr, Port) +bind(#socket{ref = SockRef}, Addr, Port) when (is_tuple(Addr) andalso ((size(Addr) =:= 4) orelse (size(Addr) =:= 8))) orelse ((Addr =:= any) orelse (Addr =:= loopback)) andalso @@ -385,7 +389,7 @@ connect(Socket, Addr, Port) -> connect(_Socket, _Addr, _Port, Timeout) when (is_integer(Timeout) andalso (Timeout =< 0)) -> {error, timeout}; -connect({socket, _, SockRef}, Addr, Port, Timeout) +connect(#socket{ref = SockRef}, Addr, Port, Timeout) when (is_tuple(Addr) andalso ((size(Addr) =:= 4) orelse (size(Addr) =:= 8))) andalso (is_integer(Port) andalso (Port >= 0)) andalso @@ -424,7 +428,7 @@ connect({socket, _, SockRef}, Addr, Port, Timeout) listen(Socket) -> listen(Socket, ?SOCKET_LISTEN_BACKLOG_DEFAULT). -listen({socket, _, SockRef}, Backlog) +listen(#socket{ref = SockRef}, Backlog) when (is_integer(Backlog) andalso (Backlog >= 0)) -> nif_listen(SockRef, Backlog). @@ -448,7 +452,7 @@ accept(Socket) -> %% Do we really need this optimization? accept(_, Timeout) when is_integer(Timeout) andalso (Timeout =< 0) -> {error, timeout}; -accept({socket, SI, LSockRef}, Timeout) +accept(#socket{info = SI, ref = LSockRef}, Timeout) when is_integer(Timeout) orelse (Timeout =:= infinity) -> do_accept(LSockRef, SI, Timeout). @@ -460,7 +464,8 @@ do_accept(LSockRef, SI, Timeout) -> SocketInfo = #{domain => maps:get(domain, SI), type => maps:get(type, SI), protocol => maps:get(protocol, SI)}, - Socket = {socket, SocketInfo, SockRef}, + Socket = #socket{info = SocketInfo, + ref = SockRef}, {ok, Socket}; {error, eagain} -> NewTimeout = next_timeout(TS, Timeout), @@ -499,9 +504,10 @@ send(Socket, Data, Timeout) -> send(Socket, Data, Flags, Timeout) when is_list(Data) -> Bin = erlang:list_to_binary(Data), send(Socket, Bin, Flags, Timeout); -send(Socket, Data, Flags, Timeout) when is_binary(Data) andalso is_list(Flags) -> +send(#socket{ref = SockRef}, Data, Flags, Timeout) + when is_binary(Data) andalso is_list(Flags) -> EFlags = enc_send_flags(Flags), - do_send(Socket, Data, EFlags, Timeout). + do_send(SockRef, Data, EFlags, Timeout). do_send(SockRef, Data, EFlags, Timeout) -> TS = timestamp(Timeout), @@ -562,14 +568,14 @@ sendto(Socket, Data, Flags, DestAddr, DestPort) -> sendto(Socket, Data, Flags, DestAddr, DestPort, Timeout) when is_list(Data) -> Bin = erlang:list_to_binary(Data), sendto(Socket, Bin, Flags, DestAddr, DestPort, Timeout); -sendto(Socket, Data, Flags, DestAddr, DestPort, Timeout) +sendto(#socket{ref = SockRef}, Data, Flags, DestAddr, DestPort, Timeout) when is_binary(Data) andalso is_list(Flags) andalso (is_tuple(DestAddr) orelse (DestAddr =:= null)) andalso is_integer(DestPort) andalso (is_integer(Timeout) orelse (Timeout =:= infinity)) -> EFlags = enc_send_flags(Flags), - do_sendto(Socket, Data, EFlags, DestAddr, DestPort, Timeout). + do_sendto(SockRef, Data, EFlags, DestAddr, DestPort, Timeout). do_sendto(SockRef, Data, EFlags, DestAddr, DestPort, Timeout) -> TS = timestamp(Timeout), @@ -726,18 +732,18 @@ recv(Socket, Length, Timeout) -> Data :: binary(), Reason :: term(). -recv(Socket, Length, Flags, Timeout) +recv(#socket{ref = SockRef}, Length, Flags, Timeout) when (is_integer(Length) andalso (Length >= 0)) andalso is_list(Flags) andalso (is_integer(Timeout) orelse (Timeout =:= infinity)) -> EFlags = enc_recv_flags(Flags), - do_recv(Socket, undefined, Length, EFlags, <<>>, EFlags). + do_recv(SockRef, undefined, Length, EFlags, <<>>, Timeout). %% We need to pass the "old recv ref" around because of the special case %% with Length = 0. This case makes it neccessary to have a timeout function %% clause since we may never wait for anything (no receive select), and so the %% the only timeout check will be the function clause. -do_recv({socket, _, SockRef} = Socket, _OldRef, Length, EFlags, Acc, Timeout) +do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) when (Timeout =:= infinity) orelse (is_integer(Timeout) andalso (Timeout > 0)) -> TS = timestamp(Timeout), @@ -754,7 +760,7 @@ do_recv({socket, _, SockRef} = Socket, _OldRef, Length, EFlags, Acc, Timeout) %% > 0 - We got a part of the message and we will be notified %% when there is more to read (a select message) {ok, false = _Complete, Bin} when (Length =:= 0) -> - do_recv(Socket, RecvRef, + do_recv(SockRef, RecvRef, Length, EFlags, <>, next_timeout(TS, Timeout)); @@ -766,7 +772,7 @@ do_recv({socket, _, SockRef} = Socket, _OldRef, Length, EFlags, Acc, Timeout) NewTimeout = next_timeout(TS, Timeout), receive {select, SockRef, RecvRef, ready_input} -> - do_recv(Socket, RecvRef, + do_recv(SockRef, RecvRef, Length-size(Bin), EFlags, Bin, next_timeout(TS, Timeout)) @@ -781,7 +787,7 @@ do_recv({socket, _, SockRef} = Socket, _OldRef, Length, EFlags, Acc, Timeout) NewTimeout = next_timeout(TS, Timeout), receive {select, SockRef, RecvRef, ready_input} -> - do_recv(Socket, RecvRef, + do_recv(SockRef, RecvRef, Length-size(Bin), EFlags, <>, next_timeout(TS, Timeout)) @@ -801,7 +807,7 @@ do_recv({socket, _, SockRef} = Socket, _OldRef, Length, EFlags, Acc, Timeout) NewTimeout = next_timeout(TS, Timeout), receive {select, SockRef, RecvRef, ready_input} -> - do_recv(Socket, RecvRef, + do_recv(SockRef, RecvRef, Length, EFlags, Acc, next_timeout(TS, Timeout)) @@ -819,37 +825,92 @@ do_recv({socket, _, SockRef} = Socket, _OldRef, Length, EFlags, Acc, Timeout) end; -do_recv({socket, _, SockRef} = _Socket, RecvRef, - 0 = _Length, _Eflags, Acc, _Timeout) -> +do_recv(SockRef, RecvRef, 0 = _Length, _Eflags, Acc, _Timeout) -> %% The current recv operation is to be cancelled, so no need for a ref... %% The cancel will end our 'read everything you have' and "activate" %% any waiting reader. nif_cancel(SockRef, recv, RecvRef), {ok, Acc}; -do_recv(_Socket, _RecvRef, _Length, _EFlags, Acc, _Timeout) when (size(Acc) > 0) -> +do_recv(_SockRef, _RecvRef, _Length, _EFlags, Acc, _Timeout) when (size(Acc) > 0) -> {error, {timeout, Acc}}; -do_recv(_Socket, _RecvRef, _Length, _EFlags, _Acc, _Timeout) -> +do_recv(_SockRef, _RecvRef, _Length, _EFlags, _Acc, _Timeout) -> {error, timeout}. %% --------------------------------------------------------------------------- %% +%% With recvfrom we get messages, which means that regardless of how +%% much we want to read, we return when we get a message. +%% The MaxSize argument basically defines the size of our receive +%% buffer. By setting the size to zero (0), we use the configured +%% size (see setopt). +%% It may be impossible to know what (buffer) size is appropriate +%% "in advance", and in those cases it may be convenient to use the +%% (recv) 'peek' flag. When this flag is provided the message is *not* +%% "consumed" from the underlying buffers, so another recvfrom call +%% is needed, possibly with a then adjusted buffer size. +%% recvfrom(Socket) -> - recvfrom(Socket, ?SOCKET_RECV_FLAGS_DEFAULT). + recvfrom(Socket, 0). + +recvfrom(Socket, BufSz) -> + recvfrom(Socket, BufSz, + ?SOCKET_RECV_FLAGS_DEFAULT, + ?SOCKET_RECV_TIMEOUT_DEFAULT). + + +recvfrom(Socket, Flags, Timeout) when is_list(Flags) -> + recvfrom(Socket, 0, Flags, Timeout); +recvfrom(Socket, BufSz, Flags) when is_list(Flags) -> + recvfrom(Socket, BufSz, Flags, ?SOCKET_RECV_TIMEOUT_DEFAULT); +recvfrom(Socket, BufSz, Timeout) -> + recvfrom(Socket, BufSz, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout). + +-spec recvfrom(Socket, BufSz, Flags, Timeout) -> {ok, {SrcDomain, Source, Data}} | {error, Reason} when + Socket :: socket(), + BufSz :: non_neg_integer(), + Flags :: recv_flags(), + Timeout :: timeout(), + SrcDomain :: domain() | undefined, + Source :: {ip_address(), port_number()} | string() | undefined, + Data :: binary(), + Reason :: term(). + +recvfrom(#socket{ref = SockRef}, BufSz, Flags, Timeout) + when (is_integer(BufSz) andalso (BufSz >= 0)) andalso + is_list(Flags) andalso + (is_integer(Timeout) orelse (Timeout =:= infinity)) -> + EFlags = enc_recv_flags(Flags), + do_recvfrom(SockRef, BufSz, EFlags, Timeout). --spec recvfrom(Socket, Flags) -> {ok, Data, SrcAddr, SrcPort} | {error, Reason} when - Socket :: socket(), - Flags :: recv_flags(), - Data :: binary(), - SrcAddr :: ip_address(), - SrcPort :: port_number(), - Reason :: term(). +do_recvfrom(SockRef, BufSz, EFlags, Timeout) -> + TS = timestamp(Timeout), + RecvRef = make_ref(), + case nif_recvfrom(SockRef, RecvRef, BufSz, EFlags) of + {ok, {_Domain, _Source, _NewData}} = OK -> + OK; + + {error, eagain} -> + %% There is nothing just now, but we will be notified when there + %% is something to read (a select message). + NewTimeout = next_timeout(TS, Timeout), + receive + {select, SockRef, RecvRef, ready_input} -> + do_recvfrom(SockRef, BufSz, EFlags, + next_timeout(TS, Timeout)) + after NewTimeout -> + nif_cancel(SockRef, recvfrom, RecvRef), + flush_select_msgs(SockRef, RecvRef), + {error, timeout} + end; + + {error, _} = ERROR -> + ERROR + + end. -recvfrom({socket, _, SockRef}, Flags) when is_list(Flags) -> - EFlags = enc_recv_flags(Flags), - nif_recvfrom(SockRef, EFlags). %% --------------------------------------------------------------------------- @@ -998,7 +1059,6 @@ enc_protocol(Type, Proto) -> throw({error, {invalid_protocol, {Type, Proto}} enc_send_flags(Flags) -> EFlags = [{confirm, ?SOCKET_SEND_FLAG_CONFIRM}, {dontroute, ?SOCKET_SEND_FLAG_DONTROUTE}, - {dontwait, ?SOCKET_SEND_FLAG_DONTWAIT}, {eor, ?SOCKET_SEND_FLAG_EOR}, {more, ?SOCKET_SEND_FLAG_MORE}, {nosignal, ?SOCKET_SEND_FLAG_NOSIGNAL}, @@ -1010,12 +1070,10 @@ enc_send_flags(Flags) -> enc_recv_flags(Flags) -> EFlags = [{cmsg_cloexec, ?SOCKET_RECV_FLAG_CMSG_CLOEXEC}, - {dontwait, ?SOCKET_RECV_FLAG_DONTWAIT}, {errqueue, ?SOCKET_RECV_FLAG_ERRQUEUE}, {oob, ?SOCKET_RECV_FLAG_OOB}, {peek, ?SOCKET_RECV_FLAG_PEEK}, - {trunc, ?SOCKET_RECV_FLAG_TRUNC}, - {waitall, ?SOCKET_RECV_FLAG_WAITALL}], + {trunc, ?SOCKET_RECV_FLAG_TRUNC}], enc_flags(Flags, EFlags). @@ -1159,7 +1217,7 @@ nif_sendto(_SRef, _SendRef, _Data, _Flags, _Dest, _Port) -> nif_recv(_SRef, _RecvRef, _Length, _Flags) -> erlang:error(badarg). -nif_recvfrom(_SRef, _Flags) -> +nif_recvfrom(_SRef, _RecvRef, _Length, _Flags) -> erlang:error(badarg). nif_cancel(_SRef, _Op, _Ref) -> -- cgit v1.2.3 From 599a320f630991823fc28b6a8a9f09851e261fed Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 24 Apr 2018 17:38:52 +0200 Subject: [socket-nif] "Completed" the close function There is probably a lot of things left to be here. For instance the handling of ECONNRESET when reading (recv and recvfrom). Also some stuff about setopt and getopt. --- erts/preloaded/src/socket.erl | 123 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 101 insertions(+), 22 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index bae561cd51..044fe73906 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -45,10 +45,8 @@ close/1, - setopt/3, - getopt/2, - %% ????? - formated_timestamp/0 + setopt/4, + getopt/3 ]). -export_type([ @@ -105,6 +103,14 @@ -type port_number() :: 0..65535. +%% otp - The option is internal to our (OTP) imeplementation. +%% socket - The socket layer (SOL_SOCKET). +%% ip - The ip layer (SOL_IP). +%% tcp - The TCP (Transport Control Protocol) layer (IPPROTO_TCP). +%% udp - The UDP (User Datagram Protocol) layer (IPPROTO_UDP). +%% Int - Raw level, sent down and used "as is". +-type option_level() :: otp | socket | ip | tcp | udp | non_neg_integer(). + -type socket_info() :: map(). -record(socket, {info :: socket_info(), ref :: reference()}). @@ -201,6 +207,14 @@ -define(SOCKET_RECV_FLAGS_DEFAULT, []). -define(SOCKET_RECV_TIMEOUT_DEFAULT, infinity). +-define(SOCKET_SETOPT_LEVEL_ENCODED, 0). +-define(SOCKET_SETOPT_LEVEL_RAW, 1). +-define(SOCKET_SETOPT_LEVEL_OTP, 0). +-define(SOCKET_SETOPT_LEVEL_SOCKET, 1). +-define(SOCKET_SETOPT_LEVEL_IP, 2). +-define(SOCKET_SETOPT_LEVEL_TCP, 3). +-define(SOCKET_SETOPT_LEVEL_UDP, 4). + -define(SOCKET_SETOPT_KEY_DEBUG, 0). -define(SOCKET_GETOPT_KEY_DEBUG, ?SOCKET_SETOPT_KEY_DEBUG). @@ -935,16 +949,37 @@ do_recvfrom(SockRef, BufSz, EFlags, Timeout) -> %% =========================================================================== %% %% close - close a file descriptor +%% -spec close(Socket) -> ok | {error, Reason} when Socket :: socket(), Reason :: term(). -close({socket, _, SockRef}) -> - nif_close(SockRef). +close(#socket{ref = SockRef}) -> + case nif_close(SockRef) of + ok -> + nif_finalize_close(SockRef); + {ok, CloseRef} -> + %% We must wait + receive + {close, CloseRef} -> + %% + %% + %% WHAT HAPPENS IF THIS PROCESS IS KILLED + %% BEFORE WE CAN EXECUTE THE FINAL CLOSE??? + %% + %% + nif_finalize_close(SockRef) + end; + {error, _} = ERROR -> + ERROR + end. + +%% =========================================================================== +%% %% setopt - manipulate individual properties of a socket %% %% What properties are valid depend on what kind of socket it is @@ -952,22 +987,30 @@ close({socket, _, SockRef}) -> %% If its an "invalid" option (or value), we should not crash but return some %% useful error... %% +%% +%% +%% WE NEED TOP MAKE SURE THAT THE USER DOES NOT MAKE US BLOCKING +%% AS MUCH OF THE CODE EXPECTS TO BE NON-BLOCKING!! +%% +%% --spec setopt(Socket, Key, Value) -> ok | {error, Reason} when +-spec setopt(Socket, Level, Key, Value) -> ok | {error, Reason} when Socket :: socket(), + Level :: option_level(), Key :: setopt_key(), Value :: term(), Reason :: term(). -setopt({socket, Info, SockRef}, Key, Value) -> +setopt(#socket{info = Info, ref = SockRef}, Level, Key, Value) -> try begin Domain = maps:get(domain, Info), Type = maps:get(type, Info), Protocol = maps:get(protocol, Info), - EKey = enc_setopt_key(Key, Domain, Type, Protocol), - EVal = enc_setopt_value(Key, Value, Domain, Type, Protocol), - nif_setopt(SockRef, EKey, EVal) + ELevel = enc_setopt_level(Level), + EKey = enc_setopt_key(Level, Key, Domain, Type, Protocol), + EVal = enc_setopt_value(Level, Key, Value, Domain, Type, Protocol), + nif_setopt(SockRef, ELevel, EKey, EVal) end catch throw:T -> @@ -985,24 +1028,26 @@ setopt({socket, Info, SockRef}, Key, Value) -> %% useful error... %% --spec getopt(Socket, Key) -> {ok, Value} | {error, Reason} when +-spec getopt(Socket, Level, Key) -> {ok, Value} | {error, Reason} when Socket :: socket(), + Level :: option_level(), Key :: getopt_key(), Value :: term(), Reason :: term(). -getopt({socket, Info, SockRef}, Key) -> +getopt(#socket{info = Info, ref = SockRef}, Level, Key) -> try begin Domain = maps:get(domain, Info), Type = maps:get(type, Info), Protocol = maps:get(protocol, Info), - EKey = enc_getopt_key(Key, Domain, Type, Protocol), + ELevel = enc_getopt_level(Level), + EKey = enc_getopt_key(Level, Key, Domain, Type, Protocol), %% We may need to decode the value (for the same reason %% we needed to encode the value for setopt). - case nif_getopt(SockRef, EKey) of + case nif_getopt(SockRef, ELevel, EKey) of {ok, EVal} -> - Val = dec_getopt_value(Key, EVal, Domain, Type, Protocol), + Val = dec_getopt_value(Level, Key, EVal, Domain, Type, Protocol), {ok, Val}; {error, _} = ERROR -> ERROR @@ -1090,21 +1135,52 @@ enc_flags(Flags, EFlags) -> end, lists:foldl(F, 0, Flags). +enc_setopt_level(otp) -> + {?SOCKET_SETOPT_LEVEL_ENCODED, ?SOCKET_SETOPT_LEVEL_OTP}; +enc_setopt_level(socket) -> + {?SOCKET_SETOPT_LEVEL_ENCODED, ?SOCKET_SETOPT_LEVEL_SOCKET}; +enc_setopt_level(ip) -> + {?SOCKET_SETOPT_LEVEL_ENCODED, ?SOCKET_SETOPT_LEVEL_IP}; +enc_setopt_level(tcp) -> + {?SOCKET_SETOPT_LEVEL_ENCODED, ?SOCKET_SETOPT_LEVEL_TCP}; +enc_setopt_level(udp) -> + {?SOCKET_SETOPT_LEVEL_ENCODED, ?SOCKET_SETOPT_LEVEL_UDP}; +%% Any option that is of an raw level must be provided as a binary +%% already fully encoded! +enc_setopt_level(L) when is_integer(L) -> + {?SOCKET_SETOPT_LEVEL_RAW, L}. + + %% We should ...really... do something with the domain, type and protocol args... -enc_setopt_key(debug, _, _, _) -> +%% Also, any option which has an integer level (raw) must also be provided +%% in a raw mode, that is, as an integer. +enc_setopt_key(L, K, _, _, _) when is_integer(L) andalso is_integer(K) -> + K; +enc_setopt_key(otp, debug, _, _, _) -> ?SOCKET_SETOPT_KEY_DEBUG. %% We should ...really... do something with the domain, type and protocol args... -enc_setopt_value(debug, V, _, _, _) when is_boolean(V) -> +enc_setopt_value(otp, debug, V, _, _, _) when is_boolean(V) -> + V; +enc_setopt_value(socket, linger, abort, D, T, P) -> + enc_setopt_value(socket, linger, {true, 0}, D, T, P); +enc_setopt_value(socket, linger, {OnOff, Secs} = V, _D, _T, _P) + when is_boolean(OnOff) andalso is_integer(Secs) andalso (Secs >= 0) -> + V; +enc_setopt_value(L, _, V, _, _, _) when is_integer(L) andalso is_binary(V) -> V. + +enc_getopt_level(Level) -> + enc_setopt_level(Level). + %% We should ...really... do something with the domain, type and protocol args... -enc_getopt_key(debug, _, _, _) -> +enc_getopt_key(otp, debug, _, _, _) -> ?SOCKET_GETOPT_KEY_DEBUG. %% We should ...really... do something with the domain, type and protocol args... -dec_getopt_value(debug, B, _, _, _) when is_boolean(B) -> +dec_getopt_value(otp, debug, B, _, _, _) when is_boolean(B) -> B. @@ -1226,8 +1302,11 @@ nif_cancel(_SRef, _Op, _Ref) -> nif_close(_SRef) -> erlang:error(badarg). -nif_setopt(_Ref, _Key, _Val) -> +nif_finalize_close(_SRef) -> + erlang:error(badarg). + +nif_setopt(_Ref, _Lev, _Key, _Val) -> erlang:error(badarg). -nif_getopt(_Ref, _Key) -> +nif_getopt(_Ref, _Lev, _Key) -> erlang:error(badarg). -- cgit v1.2.3 From 82bfbdd7919e1aee360b76bdbaca17d2cf00ee73 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 25 Apr 2018 14:54:03 +0200 Subject: [socket-nif] More close-related work There are still some questions regarding what hapopens when writing / reading from an (remote) closed socket (I talking about "properly" closed sockets). --- erts/preloaded/src/socket.erl | 62 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 12 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 044fe73906..f1e8a8b099 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -105,11 +105,12 @@ %% otp - The option is internal to our (OTP) imeplementation. %% socket - The socket layer (SOL_SOCKET). -%% ip - The ip layer (SOL_IP). +%% ip - The IP layer (SOL_IP). +%% ipv6 - The IPv6 layer (SOL_IPV6). %% tcp - The TCP (Transport Control Protocol) layer (IPPROTO_TCP). %% udp - The UDP (User Datagram Protocol) layer (IPPROTO_UDP). %% Int - Raw level, sent down and used "as is". --type option_level() :: otp | socket | ip | tcp | udp | non_neg_integer(). +-type option_level() :: otp | socket | ip | ipv6 | tcp | udp | non_neg_integer(). -type socket_info() :: map(). -record(socket, {info :: socket_info(), @@ -212,8 +213,9 @@ -define(SOCKET_SETOPT_LEVEL_OTP, 0). -define(SOCKET_SETOPT_LEVEL_SOCKET, 1). -define(SOCKET_SETOPT_LEVEL_IP, 2). --define(SOCKET_SETOPT_LEVEL_TCP, 3). --define(SOCKET_SETOPT_LEVEL_UDP, 4). +-define(SOCKET_SETOPT_LEVEL_IPV6, 3). +-define(SOCKET_SETOPT_LEVEL_TCP, 4). +-define(SOCKET_SETOPT_LEVEL_UDP, 5). -define(SOCKET_SETOPT_KEY_DEBUG, 0). @@ -485,7 +487,11 @@ do_accept(LSockRef, SI, Timeout) -> NewTimeout = next_timeout(TS, Timeout), receive {select, LSockRef, AccRef, ready_input} -> - do_accept(LSockRef, SI, next_timeout(TS, Timeout)) + do_accept(LSockRef, SI, next_timeout(TS, Timeout)); + + {nif_abort, AccRef, Reason} -> + {error, Reason} + after NewTimeout -> nif_cancel(LSockRef, accept, AccRef), flush_select_msgs(LSockRef, AccRef), @@ -539,7 +545,11 @@ do_send(SockRef, Data, EFlags, Timeout) -> next_timeout(TS, Timeout)); {select, SockRef, SendRef, ready_output} -> do_send(SockRef, Data, EFlags, - next_timeout(TS, Timeout)) + next_timeout(TS, Timeout)); + + {nif_abort, SendRef, Reason} -> + {error, Reason} + after NewTimeout -> nif_cancel(SockRef, send, SendRef), flush_select_msgs(SockRef, SendRef), @@ -549,7 +559,11 @@ do_send(SockRef, Data, EFlags, Timeout) -> receive {select, SockRef, SendRef, ready_output} -> do_send(SockRef, Data, EFlags, - next_timeout(TS, Timeout)) + next_timeout(TS, Timeout)); + + {nif_abort, SendRef, Reason} -> + {error, Reason} + after Timeout -> nif_cancel(SockRef, send, SendRef), flush_select_msgs(SockRef, SendRef), @@ -608,7 +622,11 @@ do_sendto(SockRef, Data, EFlags, DestAddr, DestPort, Timeout) -> next_timeout(TS, Timeout)); {select, SockRef, SendRef, ready_output} -> do_sendto(SockRef, Data, EFlags, DestAddr, DestPort, - next_timeout(TS, Timeout)) + next_timeout(TS, Timeout)); + + {nif_abort, SendRef, Reason} -> + {error, Reason} + after Timeout -> nif_cancel(SockRef, sendto, SendRef), flush_select_msgs(SockRef, SendRef), @@ -789,7 +807,12 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) do_recv(SockRef, RecvRef, Length-size(Bin), EFlags, Bin, - next_timeout(TS, Timeout)) + next_timeout(TS, Timeout)); + + {nif_abort, RecvRef, Reason} -> + {error, Reason} + + after NewTimeout -> nif_cancel(SockRef, recv, RecvRef), flush_select_msgs(SockRef, RecvRef), @@ -804,7 +827,12 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) do_recv(SockRef, RecvRef, Length-size(Bin), EFlags, <>, - next_timeout(TS, Timeout)) + next_timeout(TS, Timeout)); + + {nif_abort, RecvRef, Reason} -> + {error, Reason} + + after NewTimeout -> nif_cancel(SockRef, recv, RecvRef), flush_select_msgs(SockRef, RecvRef), @@ -824,7 +852,11 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) do_recv(SockRef, RecvRef, Length, EFlags, Acc, - next_timeout(TS, Timeout)) + next_timeout(TS, Timeout)); + + {nif_abort, RecvRef, Reason} -> + {error, Reason} + after NewTimeout -> nif_cancel(SockRef, recv, RecvRef), flush_select_msgs(SockRef, RecvRef), @@ -913,7 +945,11 @@ do_recvfrom(SockRef, BufSz, EFlags, Timeout) -> receive {select, SockRef, RecvRef, ready_input} -> do_recvfrom(SockRef, BufSz, EFlags, - next_timeout(TS, Timeout)) + next_timeout(TS, Timeout)); + + {nif_abort, RecvRef, Reason} -> + {error, Reason} + after NewTimeout -> nif_cancel(SockRef, recvfrom, RecvRef), flush_select_msgs(SockRef, RecvRef), @@ -1141,6 +1177,8 @@ enc_setopt_level(socket) -> {?SOCKET_SETOPT_LEVEL_ENCODED, ?SOCKET_SETOPT_LEVEL_SOCKET}; enc_setopt_level(ip) -> {?SOCKET_SETOPT_LEVEL_ENCODED, ?SOCKET_SETOPT_LEVEL_IP}; +enc_setopt_level(ipv6) -> + {?SOCKET_SETOPT_LEVEL_ENCODED, ?SOCKET_SETOPT_LEVEL_IPV6}; enc_setopt_level(tcp) -> {?SOCKET_SETOPT_LEVEL_ENCODED, ?SOCKET_SETOPT_LEVEL_TCP}; enc_setopt_level(udp) -> -- cgit v1.2.3 From b69436fc5970ff8fc749a37351e9d9c5a54f445e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 26 Apr 2018 16:16:24 +0200 Subject: [socket-nif] Completed the shutdown function --- erts/preloaded/src/socket.erl | 49 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index f1e8a8b099..8dce86c518 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -44,6 +44,7 @@ %% readv/3, close/1, + shutdown/2, setopt/4, getopt/3 @@ -65,7 +66,9 @@ accept_flag/0, send_flags/0, - send_flag/0 + send_flag/0, + + shutdown_how/0 ]). @@ -148,6 +151,8 @@ -type setopt_key() :: foo. -type getopt_key() :: foo. +-type shutdown_how() :: read | write | read_write. + -record(msg_hdr, { %% Optional address @@ -221,6 +226,9 @@ -define(SOCKET_GETOPT_KEY_DEBUG, ?SOCKET_SETOPT_KEY_DEBUG). +-define(SOCKET_SHUTDOWN_HOW_READ, 0). +-define(SOCKET_SHUTDOWN_HOW_WRITE, 1). +-define(SOCKET_SHUTDOWN_HOW_READ_WRITE, 2). %% =========================================================================== @@ -1014,6 +1022,32 @@ close(#socket{ref = SockRef}) -> +%% =========================================================================== +%% +%% shutdown - shut down part of a full-duplex connection +%% + +-spec shutdown(Socket, How) -> ok | {error, Reason} when + Socket :: socket(), + How :: shutdown_how(), + Reason :: term(). + +shutdown(#socket{ref = SockRef}, How) -> + try + begin + EHow = enc_shutdown_how(How), + nif_shutdown(SockRef, EHow) + end + catch + throw:T -> + T; + error:Reason -> + {error, Reason} + end. + + + + %% =========================================================================== %% %% setopt - manipulate individual properties of a socket @@ -1223,6 +1257,16 @@ dec_getopt_value(otp, debug, B, _, _, _) when is_boolean(B) -> +enc_shutdown_how(read) -> + ?SOCKET_SHUTDOWN_HOW_READ; +enc_shutdown_how(write) -> + ?SOCKET_SHUTDOWN_HOW_WRITE; +enc_shutdown_how(read_write) -> + ?SOCKET_SHUTDOWN_HOW_READ_WRITE. + + + + %% =========================================================================== %% %% Misc utility functions @@ -1340,6 +1384,9 @@ nif_cancel(_SRef, _Op, _Ref) -> nif_close(_SRef) -> erlang:error(badarg). +nif_shutdown(_SRef, _How) -> + erlang:error(badarg). + nif_finalize_close(_SRef) -> erlang:error(badarg). -- cgit v1.2.3 From 8e14247bc5faf5abf67901b6ae5028f2b1897c65 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 4 May 2018 16:00:26 +0200 Subject: [socket-nif] Preliminary setopt *Very* partial setopt implementation. --- erts/preloaded/src/socket.erl | 721 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 668 insertions(+), 53 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 8dce86c518..1090380769 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -20,6 +20,8 @@ -module(socket). +-compile({no_auto_import,[error/1]}). + %% Administrative and "global" utility functions -export([ on_load/0, on_load/1, on_load/2, @@ -68,7 +70,18 @@ send_flags/0, send_flag/0, - shutdown_how/0 + shutdown_how/0, + + sockopt_level/0, + otp_socket_option/0, + socket_option/0, + ip_socket_option/0, + ipv6_socket_option/0, + tcp_socket_option/0, + udp_socket_option/0, + sctp_socket_option/0, + + ip_tos_flag/0 ]). @@ -108,12 +121,189 @@ %% otp - The option is internal to our (OTP) imeplementation. %% socket - The socket layer (SOL_SOCKET). -%% ip - The IP layer (SOL_IP). +%% ip - The IP layer (SOL_IP or is it IPPROTO_IP?). %% ipv6 - The IPv6 layer (SOL_IPV6). %% tcp - The TCP (Transport Control Protocol) layer (IPPROTO_TCP). %% udp - The UDP (User Datagram Protocol) layer (IPPROTO_UDP). +%% sctp - The SCTP (Stream Control Transmission Protocol) layer (IPPROTO_SCTP). %% Int - Raw level, sent down and used "as is". --type option_level() :: otp | socket | ip | ipv6 | tcp | udp | non_neg_integer(). +-type sockopt_level() :: otp | + socket | + ip | ipv6 | tcp | udp | sctp | + non_neg_integer(). + +%% There are some options that are 'read-only'. +%% Should those be included here or in a special list? +%% Should we just document it and leave it to the user? +%% Or catch it in the encode functions? +%% A setopt for a readonly option leads to einval? + +-type otp_socket_option() :: debug | + iow | + rcvbuf | + sndbuf. +%% Shall we have special treatment of linger?? +%% read-only options: +%% domain | protocol | type. +%% FreeBSD (only?): acceptfilter +-type socket_option() :: acceptconn | + acceptfilter | + bindtodevice | + broadcast | + busy_poll | + debug | + dontroute | + error | + keepalive | + linger | + mark | + oobinline | + passcred | + peek_off | + peek_cred | + priority | + rcvbuf | + rcvbufforce | + rcvlowat | sndlowat | + rcvtimeo | sndtimeo | + reuseaddr | + reuseport | + rxq_ovfl | + setfib | + sndbuf | + sndbufforce | + timestamp | + type. +%% Read-only options: +%% mtu +%% +%% Options only valid for RAW sockets: +%% nodefrag +-type ip_socket_option() :: add_membership | + add_source_membership | + block_source | + dont_frag | + drop_membership | + drop_source_membership | + freebind | + hdrincl | + minttl | + msfilter | + mtu | + mtu_discover | + multicast_all | + multicast_if | + multicast_loop | + multicast_ttl | + nodefrag | + options | + pktinfo | + recverr | + recvif | + recvdstaddr | + recvopts | + recvorigdstaddr | + recvtos | + recvttl | + retopts | + router_alert | + sndsrcaddr | + tos | + transparent | + ttl | + unblock_source. +-type ipv6_socket_option() :: + addform | + add_membership | drop_membership | + authhdr | + auth_level | + checksum | + dstopts | + esp_trans_level | + esp_network_level | + faith | + flowinfo | + hoplimit | + hopopts | + ipcomp_level | + join_group | + leave_group | + mtu | + mtu_discover | + multicast_hops | + multicast_if | + multicast_loop | + portrange | + pktinfo | + pktoptions | + recverr | + recvpktinfo | + recvtclass | + router_alert | + rthdr | + tclass | + unicast_hops | + use_min_mtu | + v6only. + +-type tcp_socket_option() :: congestion | + maxseg | + nodelay | + user_timeout. + +-type udp_socket_option() :: checksum | + maxdgram | + recvspace. +-type sctp_socket_option() :: + adaption_layer | + associnfo | + auth_active_key | + auth_asconf | + auth_chunk | + auth_key | + auth_delete_key | + autoclose | + context | + default_send_params | + delayed_ack_time | + disable_fragments | + hmac_ident | + events | + explicit_eor | + fragment_interleave | + get_peer_addr_info | + initmsg | + i_want_mapped_v4_addr | + local_auth_chunks | + maxseg | + maxburst | + nodelay | + partial_delivery_point | + peer_addr_params | + peer_auth_chunks | + primary_addr | + reset_streams | + rtoinfo | + set_peer_primary_addr | + status | + use_ext_recvinfo. + +%% -type plain_socket_options() :: integer(). +%% -type sockopts() :: otp_socket_options() | +%% socket_options() | +%% ip_socket_options() | +%% ipv6_socket_options() | +%% tcp_socket_options() | +%% udp_socket_options() | +%% sctp_socket_options() | +%% plain_socket_options(). + +%% If the integer value is used its up to the caller to ensure its valid! +-type ip_tos_flag() :: lowdeley | + throughput | + reliability | + mincost | + integer(). -type socket_info() :: map(). -record(socket, {info :: socket_info(), @@ -148,9 +338,6 @@ peek | trunc. --type setopt_key() :: foo. --type getopt_key() :: foo. - -type shutdown_how() :: read | write | read_write. -record(msg_hdr, @@ -213,24 +400,35 @@ -define(SOCKET_RECV_FLAGS_DEFAULT, []). -define(SOCKET_RECV_TIMEOUT_DEFAULT, infinity). --define(SOCKET_SETOPT_LEVEL_ENCODED, 0). --define(SOCKET_SETOPT_LEVEL_RAW, 1). --define(SOCKET_SETOPT_LEVEL_OTP, 0). --define(SOCKET_SETOPT_LEVEL_SOCKET, 1). --define(SOCKET_SETOPT_LEVEL_IP, 2). --define(SOCKET_SETOPT_LEVEL_IPV6, 3). --define(SOCKET_SETOPT_LEVEL_TCP, 4). --define(SOCKET_SETOPT_LEVEL_UDP, 5). +-define(SOCKET_OPT_LEVEL_OTP, 0). +-define(SOCKET_OPT_LEVEL_SOCKET, 1). +-define(SOCKET_OPT_LEVEL_IP, 2). +-define(SOCKET_OPT_LEVEL_IPV6, 3). +-define(SOCKET_OPT_LEVEL_TCP, 4). +-define(SOCKET_OPT_LEVEL_UDP, 5). +-define(SOCKET_OPT_LEVEL_SCTP, 6). --define(SOCKET_SETOPT_KEY_DEBUG, 0). +-define(SOCKET_OPT_OTP_DEBUG, 0). +-define(SOCKET_OPT_OTP_IOW, 1). --define(SOCKET_GETOPT_KEY_DEBUG, ?SOCKET_SETOPT_KEY_DEBUG). +-define(SOCKET_OPT_SOCK_KEEPALIVE, 0). +-define(SOCKET_OPT_SOCK_LINGER, 1). + +-define(SOCKET_OPT_IP_RECVTOS, 0). +-define(SOCKET_OPT_IP_ROUTER_ALERT, 1). +-define(SOCKET_OPT_IP_TOS, 2). +-define(SOCKET_OPT_IP_TTL, 3). + +-define(SOCKET_OPT_IPV6_HOPLIMIT, 0). + +-define(SOCKET_OPT_TCP_MAXSEG, 0). -define(SOCKET_SHUTDOWN_HOW_READ, 0). -define(SOCKET_SHUTDOWN_HOW_WRITE, 1). -define(SOCKET_SHUTDOWN_HOW_READ_WRITE, 2). + %% =========================================================================== %% %% Administrative and utility API @@ -1059,28 +1257,64 @@ shutdown(#socket{ref = SockRef}, How) -> %% %% %% -%% WE NEED TOP MAKE SURE THAT THE USER DOES NOT MAKE US BLOCKING +%% WE NEED TO MAKE SURE THAT THE USER DOES NOT MAKE US BLOCKING %% AS MUCH OF THE CODE EXPECTS TO BE NON-BLOCKING!! %% %% --spec setopt(Socket, Level, Key, Value) -> ok | {error, Reason} when +-spec setopt(Socket, Level, Opt, Value) -> ok | {error, Reason} when + Socket :: socket(), + Level :: otp, + Opt :: otp_socket_option(), + Value :: term(), + Reason :: term() + ; (Socket, Level, Opt, Value) -> ok | {error, Reason} when + Socket :: socket(), + Level :: socket, + Opt :: socket_option(), + Value :: term(), + Reason :: term() + ; (Socket, Level, Opt, Value) -> ok | {error, Reason} when + Socket :: socket(), + Level :: ip, + Opt :: ip_socket_option(), + Value :: term(), + Reason :: term() + ; (Socket, Level, Opt, Value) -> ok | {error, Reason} when + Socket :: socket(), + Level :: ipv6, + Opt :: ipv6_socket_option(), + Value :: term(), + Reason :: term() + ; (Socket, Level, Opt, Value) -> ok | {error, Reason} when + Socket :: socket(), + Level :: tcp, + Opt :: tcp_socket_option(), + Value :: term(), + Reason :: term() + ; (Socket, Level, Opt, Value) -> ok | {error, Reason} when + Socket :: socket(), + Level :: udp, + Opt :: udp_socket_option(), + Value :: term(), + Reason :: term() + ; (Socket, Level, Opt, Value) -> ok | {error, Reason} when Socket :: socket(), - Level :: option_level(), - Key :: setopt_key(), + Level :: sctp, + Opt :: sctp_socket_option(), Value :: term(), Reason :: term(). setopt(#socket{info = Info, ref = SockRef}, Level, Key, Value) -> try begin - Domain = maps:get(domain, Info), - Type = maps:get(type, Info), - Protocol = maps:get(protocol, Info), - ELevel = enc_setopt_level(Level), - EKey = enc_setopt_key(Level, Key, Domain, Type, Protocol), - EVal = enc_setopt_value(Level, Key, Value, Domain, Type, Protocol), - nif_setopt(SockRef, ELevel, EKey, EVal) + Domain = maps:get(domain, Info), + Type = maps:get(type, Info), + Protocol = maps:get(protocol, Info), + {EIsEncoded, ELevel} = enc_setopt_level(Level), + EKey = enc_setopt_key(Level, Key, Domain, Type, Protocol), + EVal = enc_setopt_value(Level, Key, Value, Domain, Type, Protocol), + nif_setopt(SockRef, EIsEncoded, ELevel, EKey, EVal) end catch throw:T -> @@ -1090,6 +1324,9 @@ setopt(#socket{info = Info, ref = SockRef}, Level, Key, Value) -> end. + +%% =========================================================================== +%% %% getopt - retrieve individual properties of a socket %% %% What properties are valid depend on what kind of socket it is @@ -1100,8 +1337,44 @@ setopt(#socket{info = Info, ref = SockRef}, Level, Key, Value) -> -spec getopt(Socket, Level, Key) -> {ok, Value} | {error, Reason} when Socket :: socket(), - Level :: option_level(), - Key :: getopt_key(), + Level :: otp, + Key :: otp_socket_option(), + Value :: term(), + Reason :: term() + ; (Socket, Level, Key) -> {ok, Value} | {error, Reason} when + Socket :: socket(), + Level :: socket, + Key :: socket_option(), + Value :: term(), + Reason :: term() + ; (Socket, Level, Key) -> {ok, Value} | {error, Reason} when + Socket :: socket(), + Level :: ip, + Key :: ip_socket_option(), + Value :: term(), + Reason :: term() + ; (Socket, Level, Key) -> {ok, Value} | {error, Reason} when + Socket :: socket(), + Level :: ipv6, + Key :: ipv6_socket_option(), + Value :: term(), + Reason :: term() + ; (Socket, Level, Key) -> {ok, Value} | {error, Reason} when + Socket :: socket(), + Level :: tcp, + Key :: tcp_socket_option(), + Value :: term(), + Reason :: term() + ; (Socket, Level, Key) -> {ok, Value} | {error, Reason} when + Socket :: socket(), + Level :: udp, + Key :: udp_socket_option(), + Value :: term(), + Reason :: term() + ; (Socket, Level, Key) -> {ok, Value} | {error, Reason} when + Socket :: socket(), + Level :: sctp, + Key :: sctp_socket_option(), Value :: term(), Reason :: term(). @@ -1111,13 +1384,14 @@ getopt(#socket{info = Info, ref = SockRef}, Level, Key) -> Domain = maps:get(domain, Info), Type = maps:get(type, Info), Protocol = maps:get(protocol, Info), - ELevel = enc_getopt_level(Level), + {EIsEncoded, ELevel} = enc_getopt_level(Level), EKey = enc_getopt_key(Level, Key, Domain, Type, Protocol), %% We may need to decode the value (for the same reason %% we needed to encode the value for setopt). - case nif_getopt(SockRef, ELevel, EKey) of + case nif_getopt(SockRef, EIsEncoded, ELevel, EKey) of {ok, EVal} -> - Val = dec_getopt_value(Level, Key, EVal, Domain, Type, Protocol), + Val = dec_getopt_value(Level, Key, EVal, + Domain, Type, Protocol), {ok, Val}; {error, _} = ERROR -> ERROR @@ -1205,51 +1479,123 @@ enc_flags(Flags, EFlags) -> end, lists:foldl(F, 0, Flags). + +%% +++ Encode setopt level +++ + +-spec enc_setopt_level(Level) -> {IsEncoded, EncodedLevel} when + Level :: sockopt_level(), + IsEncoded :: boolean(), + EncodedLevel :: integer(). + enc_setopt_level(otp) -> - {?SOCKET_SETOPT_LEVEL_ENCODED, ?SOCKET_SETOPT_LEVEL_OTP}; + {true, ?SOCKET_OPT_LEVEL_OTP}; enc_setopt_level(socket) -> - {?SOCKET_SETOPT_LEVEL_ENCODED, ?SOCKET_SETOPT_LEVEL_SOCKET}; + {true, ?SOCKET_OPT_LEVEL_SOCKET}; enc_setopt_level(ip) -> - {?SOCKET_SETOPT_LEVEL_ENCODED, ?SOCKET_SETOPT_LEVEL_IP}; + {true, ?SOCKET_OPT_LEVEL_IP}; enc_setopt_level(ipv6) -> - {?SOCKET_SETOPT_LEVEL_ENCODED, ?SOCKET_SETOPT_LEVEL_IPV6}; + {true, ?SOCKET_OPT_LEVEL_IPV6}; enc_setopt_level(tcp) -> - {?SOCKET_SETOPT_LEVEL_ENCODED, ?SOCKET_SETOPT_LEVEL_TCP}; + {true, ?SOCKET_OPT_LEVEL_TCP}; enc_setopt_level(udp) -> - {?SOCKET_SETOPT_LEVEL_ENCODED, ?SOCKET_SETOPT_LEVEL_UDP}; -%% Any option that is of an raw level must be provided as a binary + {true, ?SOCKET_OPT_LEVEL_UDP}; +%% Any option that is of an plain level must be provided as a binary %% already fully encoded! enc_setopt_level(L) when is_integer(L) -> - {?SOCKET_SETOPT_LEVEL_RAW, L}. + {false, L}. -%% We should ...really... do something with the domain, type and protocol args... -%% Also, any option which has an integer level (raw) must also be provided -%% in a raw mode, that is, as an integer. -enc_setopt_key(L, K, _, _, _) when is_integer(L) andalso is_integer(K) -> - K; -enc_setopt_key(otp, debug, _, _, _) -> - ?SOCKET_SETOPT_KEY_DEBUG. +%% +++ Encode setopt key +++ %% We should ...really... do something with the domain, type and protocol args... +%% Also, any option (key) which has an integer level (plain) must also be provided +%% in a plain mode, that is, as an integer. +%% Also, not all options are available on all platforms. That is something we +%% don't check here, but in the nif-code. + +enc_setopt_key(Level, Opt, Domain, Type, Protocol) -> + enc_sockopt_key(Level, Opt, set, Domain, Type, Protocol). + + +%% +++ Encode setopt value +++ +%% +%% For the most part this function does *not* do an actually encode, +%% it simply validates the value type. But in some cases it actually +%% encodes the value into an more manageable type. + enc_setopt_value(otp, debug, V, _, _, _) when is_boolean(V) -> V; +enc_setopt_value(otp, iow, V, _, _, _) when is_boolean(V) -> + V; +enc_setopt_value(otp = L, Opt, V, _D, _T, _P) -> + not_supported({L, Opt, V}); + +enc_setopt_value(socket, keepalive, V, _D, _T, _P) when is_boolean(V) -> + V; enc_setopt_value(socket, linger, abort, D, T, P) -> enc_setopt_value(socket, linger, {true, 0}, D, T, P); enc_setopt_value(socket, linger, {OnOff, Secs} = V, _D, _T, _P) when is_boolean(OnOff) andalso is_integer(Secs) andalso (Secs >= 0) -> V; -enc_setopt_value(L, _, V, _, _, _) when is_integer(L) andalso is_binary(V) -> +enc_setopt_value(socket = L, Opt, V, _D, _T, _P) -> + not_supported({L, Opt, V}); + +enc_setopt_value(ip, recvtos, V, _D, _T, _P) + when is_boolean(V) -> + V; +enc_setopt_value(ip, router_alert, V, _D, _T, _P) + when is_integer(V) -> + V; +enc_setopt_value(ip, tos, V, _D, _T, _P) + when (V =:= lowdelay) orelse + (V =:= throughput) orelse + (V =:= reliability) orelse + (V =:= mincost) orelse + is_integer(V) -> + V; +enc_setopt_value(ip, ttl, V, _D, _T, _P) + when is_integer(V) -> + V; +enc_setopt_value(ip = L, Opt, V, _D, _T, _P) -> + not_supported({L, Opt, V}); + +enc_setopt_value(ipv6, hoplimit, V, _D, T, _P) + when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) -> + V; +enc_setopt_value(ipv6 = L, Opt, V, _D, _T, _P) -> + not_supported({L, Opt, V}); + +enc_setopt_value(tcp, maxseg, V, _D, T, P) + when is_integer(V) andalso + (T =:= stream) andalso + (P =:= tcp) -> + V; +enc_setopt_value(tcp = L, Opt, V, _D, _T, _P) -> + not_supported({L, Opt, V}); + +enc_setopt_value(udp = L, Opt, _V, _D, _T, _P) -> + not_supported({L, Opt}); + +enc_setopt_value(L, Opt, V, _, _, _) + when is_integer(L) andalso is_integer(Opt) andalso is_binary(V) -> V. + +%% +++ Encode getopt value +++ + enc_getopt_level(Level) -> enc_setopt_level(Level). -%% We should ...really... do something with the domain, type and protocol args... -enc_getopt_key(otp, debug, _, _, _) -> - ?SOCKET_GETOPT_KEY_DEBUG. + +%% +++ Encode getopt key +++ + +enc_getopt_key(Level, Opt, Domain, Type, Protocol) -> + enc_sockopt_key(Level, Opt, get, Domain, Type, Protocol). + + +%% +++ Decode getopt value +++ %% We should ...really... do something with the domain, type and protocol args... dec_getopt_value(otp, debug, B, _, _, _) when is_boolean(B) -> @@ -1257,6 +1603,260 @@ dec_getopt_value(otp, debug, B, _, _, _) when is_boolean(B) -> +%% +++ Encode socket option key +++ + +%% Most options are usable both for set and get, but some are +%% are only available for e.g. get. +-spec enc_sockopt_key(Level, Opt, + Direction, + Domain, Type, Protocol) -> non_neg_integer() when + Level :: otp, + Opt :: otp_socket_option(), + Direction :: set | get, + Domain :: domain(), + Type :: type(), + Protocol :: protocol() + ; (Level, Direction, Opt, + Domain, Type, Protocol) -> non_neg_integer() when + Level :: socket, + Opt :: socket_option(), + Direction :: set | get, + Domain :: domain(), + Type :: type(), + Protocol :: protocol() + ; (Level, Direction, Opt, + Domain, Type, Protocol) -> non_neg_integer() when + Level :: ip, + Opt :: ip_socket_option(), + Direction :: set | get, + Domain :: domain(), + Type :: type(), + Protocol :: protocol() + ; (Level, Direction, Opt, + Domain, Type, Protocol) -> non_neg_integer() when + Level :: ipv6, + Opt :: ipv6_socket_option(), + Direction :: set | get, + Domain :: domain(), + Type :: type(), + Protocol :: protocol() + ; (Level, Direction, Opt, + Domain, Type, Protocol) -> non_neg_integer() when + Level :: tcp, + Opt :: tcp_socket_option(), + Direction :: set | get, + Domain :: domain(), + Type :: type(), + Protocol :: protocol() + ; (Level, Direction, Opt, + Domain, Type, Protocol) -> non_neg_integer() when + Level :: udp, + Opt :: udp_socket_option(), + Direction :: set | get, + Domain :: domain(), + Type :: type(), + Protocol :: protocol() + ; (Level, Direction, Opt, + Domain, Type, Protocol) -> non_neg_integer() when + Level :: sctp, + Opt :: sctp_socket_option(), + Direction :: set | get, + Domain :: domain(), + Type :: type(), + Protocol :: protocol() + ; (Level, Direction, Opt, + Domain, Type, Protocol) -> non_neg_integer() when + Level :: integer(), + Opt :: integer(), + Direction :: set | get, + Domain :: domain(), + Type :: type(), + Protocol :: protocol(). + + +%% +++ OTP socket options +++ +enc_sockopt_key(otp, debug, _, _, _, _) -> + ?SOCKET_OPT_OTP_DEBUG; +enc_sockopt_key(otp, iow, _, _, _, _) -> + ?SOCKET_OPT_OTP_IOW; + +%% +++ SOCKET socket options +++ +enc_sockopt_key(socket, acceptconn = Opt, get = _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(socket, acceptfilter = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +%% Before linux 3.8, this socket option could be set. +%% Size of buffer for name: IFNAMSZ +%% So, we let the implementation decide. +enc_sockopt_key(socket, bindtodevide = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(socket, broadcast = Opt, _Dir, _D, dgram = _T, _P) -> + not_supported(Opt); +enc_sockopt_key(socket, busy_poll = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(socket, debug = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(socket, dontroute = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(socket, error = Opt, get = _Dir, _D, _T, _P) -> + not_supported(Opt); +%% This is only for connection-oriented sockets, but who are those? +%% Type = stream or Protocol = tcp? +%% For now, we just let is pass and it will fail later if not ok... +enc_sockopt_key(socket, keepalive = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SOCK_KEEPALIVE; +enc_sockopt_key(socket, linger = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SOCK_LINGER; +enc_sockopt_key(socket, mark = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(socket, oobinline = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(socket, passcred = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(socket, peek_off = Opt, _Dir, local = _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(socket, peek_cred = Opt, get = _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(socket, priority = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(socket, rcvbuf = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(socket, rcvbufforce = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +%% May not work on linux. +enc_sockopt_key(socket, rcvlowat = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(socket, rcvtimeo = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(socket, reuseaddr = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(socket, reuseport = Opt, _Dir, D, _T, _P) + when ((D =:= inet) orelse (D =:= inet6)) -> + not_supported(Opt); +enc_sockopt_key(socket, rxq_ovfl = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(socket, setfib = Opt, set = _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(socket, sndbuf = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(socket, sndbufforce = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +%% Not changeable on linux. +enc_sockopt_key(socket, sndlowat = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(socket, sndtimeo = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(socket, timestamp = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(socket, UnknownOpt, _Dir, _D, _T, _P) -> + unknown(UnknownOpt); + +%% +++ IP socket options +++ +enc_sockopt_key(ip, add_membership = Opt, set = _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(ip, add_source_membership = Opt, set = _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(ip, block_source = Opt, set = _Dir, _D, _T, _P) -> + not_supported(Opt); +%% FreeBSD only? +%% Only respected on udp and raw ip (unless the hdrincl option has been set). +enc_sockopt_key(ip, dontfrag = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(ip, drop_membership = Opt, set = _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(ip, drop_source_membership = Opt, set = _Dir, _D, _T, _P) -> + not_supported(Opt); +%% Linux only? +enc_sockopt_key(ip, free_bind = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(ip, hdrincl = Opt, _Dir, _D, raw = _T, _P) -> + not_supported(Opt); +%% FreeBSD only? +enc_sockopt_key(ip, minttl = Opt, _Dir, _D, raw = _T, _P) -> + not_supported(Opt); +enc_sockopt_key(ip, msfilter = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(ip, mtu = Opt, get = _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(ip, mtu_discover = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(ip, multicast_all = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(ip, multicast_if = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(ip, multicast_loop = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(ip, multicast_ttl = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(ip, nodefrag = Opt, _Dir, _D, raw = _T, _P) -> + not_supported(Opt); +enc_sockopt_key(ip, options = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(ip, pktinfo = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +%% This require special code for accessing the errors. +%% via calling the recvmsg with the MSG_ERRQUEUE flag set, +enc_sockopt_key(ip, recverr = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(ip, recvif = Opt, _Dir, _D, dgram = _T, _P) -> + not_supported(Opt); +enc_sockopt_key(ip, recvdstaddr = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(ip, recvopts = Opt, _Dir, _D, T, _P) when (T =/= stream) -> + not_supported(Opt); +enc_sockopt_key(ip, recvorigdstaddr = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(ip, recvtos = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IP_RECVTOS; +enc_sockopt_key(ip, recvttl = Opt, _Dir, _D, T, _P) when (T =/= stream) -> + not_supported(Opt); +enc_sockopt_key(ip, retopts = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(ip, router_alert = Opt, _Dir, _D, raw = _T, _P) -> + not_supported(Opt); +%% On FreeBSD it specifies that this option is only valid +%% for stream, dgram and "some" raw sockets... +%% No such condition on linux (in the man page)... +enc_sockopt_key(ip, tos = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IP_TOS; +enc_sockopt_key(ip, transparent = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(ip, ttl = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IP_TTL; +enc_sockopt_key(ip, unblock_source = Opt, set = _Dir, _D, _T, _P) -> + not_supported(Opt); +enc_sockopt_key(ip, UnknownOpt, _Dir, _D, _T, _P) -> + unknown(UnknownOpt); + +%% IPv6 socket options +enc_sockopt_key(ipv6, hoplimit = _Opt, _Dir, _D, T, _P) + when (T =:= dgram) orelse (T =:= raw) -> + ?SOCKET_OPT_IPV6_HOPLIMIT; +enc_sockopt_key(ipv6, UnknownOpt, _Dir, _D, _T, _P) -> + unknown(UnknownOpt); + +%% TCP socket options +enc_sockopt_key(tcp, UnknownOpt, _Dir, _D, _T, _P) -> + unknown(UnknownOpt); + +%% UDP socket options +enc_sockopt_key(udp, UnknownOpt, _Dir, _D, _T, _P) -> + unknown(UnknownOpt); + +%% SCTP socket options +enc_sockopt_key(sctp, UnknownOpt, _Dir, _D, _T, _P) -> + unknown(UnknownOpt); + +%% +++ Plain socket options +++ +enc_sockopt_key(Level, Opt, _Dir, _D, _T, _P) + when is_integer(Level) andalso is_integer(Opt) -> + Opt; + +enc_sockopt_key(Level, Opt, _Dir, _Domain, _Type, _Protocol) -> + unknown({Level, Opt}). + + + enc_shutdown_how(read) -> ?SOCKET_SHUTDOWN_HOW_READ; enc_shutdown_how(write) -> @@ -1336,6 +1936,21 @@ tdiff(T1, T2) -> +%% =========================================================================== +%% +%% Error functions +%% +%% =========================================================================== + +not_supported(What) -> + error({not_supported, What}). + +unknown(What) -> + error({unknown, What}). + +error(Reason) -> + throw({error, Reason}). + %% =========================================================================== %% @@ -1390,8 +2005,8 @@ nif_shutdown(_SRef, _How) -> nif_finalize_close(_SRef) -> erlang:error(badarg). -nif_setopt(_Ref, _Lev, _Key, _Val) -> +nif_setopt(_Ref, _IsEnc, _Lev, _Key, _Val) -> erlang:error(badarg). -nif_getopt(_Ref, _Lev, _Key) -> +nif_getopt(_Ref, _IsEnc, _Lev, _Key) -> erlang:error(badarg). -- cgit v1.2.3 From 0ab2ce1aad7596e81c1c59f71ccb3b2ad4f7f070 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 8 May 2018 10:16:50 +0200 Subject: [socket-nif] Network interface mappings and sock addr Add functions for mapping network interface names and indexes. Also refined the types for socket addresses: in4_sockaddr and in6_sockaddr. --- erts/preloaded/src/socket.erl | 91 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 84 insertions(+), 7 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 1090380769..5bea039783 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -49,7 +49,12 @@ shutdown/2, setopt/4, - getopt/3 + getopt/3, + + %% Some IPv6 utility functions + link_if2idx/1, + link_idx2if/1, + link_ifs/0 ]). -export_type([ @@ -61,6 +66,8 @@ ip_address/0, ip4_address/0, ip6_address/0, + in_sockaddr/0, + in4_sockaddr/0, in6_sockaddr/0, port_number/0, @@ -98,9 +105,10 @@ -type ip4_address() :: {0..255, 0..255, 0..255, 0..255}. +-type uint20() :: 0..16#FFFFF. -type uint32() :: 0..16#FFFFFFFF. --type ip6_flow_info() :: uint32(). --type ip6_scope_id() :: uint32(). +-type in6_flow_info() :: uint20(). +-type in6_scope_id() :: uint32(). -type ip6_address() :: {0..65535, @@ -111,12 +119,23 @@ 0..65535, 0..65535, 0..65535}. -%% We need to polish this further... --record(in6_sockaddr, {addr :: ip6_address(), - flowinfo = 0 :: ip6_flow_info(), - scope_id :: ip6_scope_id()}). + +%% +%% Should we do these as maps instead? +%% If we do we may need to include the family (domain) in the +%% map (as the native type do. See struct sockaddr_in6). +%% +-record(in4_sockaddr, {port = 0 :: port_number(), + addr = any :: any | loopback | ip4_address()}). +-type in4_sockaddr() :: #in4_sockaddr{}. +-record(in6_sockaddr, {port = 0 :: port_number(), + addr = any :: any | loopback | ip6_address(), + flowinfo = 0 :: in6_flow_info(), + scope_id = 0 :: in6_scope_id()}). -type in6_sockaddr() :: #in6_sockaddr{}. +-type in_sockaddr() :: in4_sockaddr() | in6_sockaddr(). + -type port_number() :: 0..65535. %% otp - The option is internal to our (OTP) imeplementation. @@ -1406,6 +1425,55 @@ getopt(#socket{info = Info, ref = SockRef}, Level, Key) -> +%% =========================================================================== +%% +%% link_if2idx - Mappings between network interface names and indexes: if -> idx +%% +%% + +-spec link_if2idx(If) -> {ok, Idx} | {error, Reason} when + If :: string(), + Idx :: non_neg_integer(), + Reason :: term(). + +link_if2idx(If) when is_list(If) -> + nif_link_if2idx(If). + + + +%% =========================================================================== +%% +%% link_idx2if - Mappings between network interface names and indexes: idx -> if +%% +%% + +-spec link_idx2if(Idx) -> {ok, If} | {error, Reason} when + Idx :: non_neg_integer(), + If :: string(), + Reason :: term(). + +link_idx2if(Idx) when is_integer(Idx) -> + nif_link_idx2if(Idx). + + + +%% =========================================================================== +%% +%% link_ifs - get network interface names and indexes +%% +%% + +-spec link_ifs() -> Names | {error, Reason} when + Names :: [{Idx, If}], + Idx :: non_neg_integer(), + If :: string(), + Reason :: term(). + +link_ifs() -> + nif_link_ifs(). + + + %% =========================================================================== %% %% Encode / decode @@ -2010,3 +2078,12 @@ nif_setopt(_Ref, _IsEnc, _Lev, _Key, _Val) -> nif_getopt(_Ref, _IsEnc, _Lev, _Key) -> erlang:error(badarg). + +nif_link_if2idx(_Name) -> + erlang:error(badarg). + +nif_link_idx2if(_Id) -> + erlang:error(badarg). + +nif_link_ifs() -> + erlang:error(badarg). -- cgit v1.2.3 From f7f70b94f90b1f500cb884be8ae722e1a22acf2e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 8 May 2018 11:47:43 +0200 Subject: [socket-nif] setopt of (tcp) congestion --- erts/preloaded/src/socket.erl | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 5bea039783..1304e79c99 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -267,8 +267,7 @@ -type tcp_socket_option() :: congestion | maxseg | - nodelay | - user_timeout. + nodelay. -type udp_socket_option() :: checksum | maxdgram | @@ -440,7 +439,8 @@ -define(SOCKET_OPT_IPV6_HOPLIMIT, 0). --define(SOCKET_OPT_TCP_MAXSEG, 0). +-define(SOCKET_OPT_TCP_CONGESTION, 0). +-define(SOCKET_OPT_TCP_MAXSEG, 1). -define(SOCKET_SHUTDOWN_HOW_READ, 0). -define(SOCKET_SHUTDOWN_HOW_WRITE, 1). @@ -1633,11 +1633,21 @@ enc_setopt_value(ipv6, hoplimit, V, _D, T, _P) enc_setopt_value(ipv6 = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); +enc_setopt_value(tcp, congetsion, V, _D, T, P) + when is_list(V) andalso + (T =:= stream) andalso + (P =:= tcp) -> + V; enc_setopt_value(tcp, maxseg, V, _D, T, P) when is_integer(V) andalso (T =:= stream) andalso (P =:= tcp) -> V; +enc_setopt_value(tcp, nodelay, V, _D, T, P) + when is_boolean(V) andalso + (T =:= stream) andalso + (P =:= tcp) -> + V; enc_setopt_value(tcp = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); @@ -1880,8 +1890,8 @@ enc_sockopt_key(ip, recvttl = Opt, _Dir, _D, T, _P) when (T =/= stream) -> not_supported(Opt); enc_sockopt_key(ip, retopts = Opt, _Dir, _D, _T, _P) -> not_supported(Opt); -enc_sockopt_key(ip, router_alert = Opt, _Dir, _D, raw = _T, _P) -> - not_supported(Opt); +enc_sockopt_key(ip, router_alert = _Opt, _Dir, _D, raw = _T, _P) -> + ?SOCKET_OPT_IP_ROUTER_ALERT; %% On FreeBSD it specifies that this option is only valid %% for stream, dgram and "some" raw sockets... %% No such condition on linux (in the man page)... @@ -1904,6 +1914,14 @@ enc_sockopt_key(ipv6, UnknownOpt, _Dir, _D, _T, _P) -> unknown(UnknownOpt); %% TCP socket options +%% There are other options that would be useful; info, +%% but they are difficult to get portable... +enc_sockopt_key(tcp, congestion = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_TCP_CONGESTION; +enc_sockopt_key(tcp, maxseg = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_TCP_MAXSEG; +enc_sockopt_key(tcp, nodelay = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); enc_sockopt_key(tcp, UnknownOpt, _Dir, _D, _T, _P) -> unknown(UnknownOpt); -- cgit v1.2.3 From fece5e536ba4e814bcfe5896e23c2ef979468c7b Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 8 May 2018 12:16:11 +0200 Subject: [socket-nif] More setopt Add handling of nodelay tcp option setopt. Added placeholder sctp options autoclose and nodelay (since my machine does not actually have sctp installed...). --- erts/preloaded/src/socket.erl | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 1304e79c99..1c811fa11a 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -233,10 +233,11 @@ unblock_source. -type ipv6_socket_option() :: addform | - add_membership | drop_membership | + add_membership | authhdr | auth_level | checksum | + drop_membership | dstopts | esp_trans_level | esp_network_level | @@ -437,10 +438,14 @@ -define(SOCKET_OPT_IP_TOS, 2). -define(SOCKET_OPT_IP_TTL, 3). --define(SOCKET_OPT_IPV6_HOPLIMIT, 0). +-define(SOCKET_OPT_IPV6_HOPLIMIT, 12). -define(SOCKET_OPT_TCP_CONGESTION, 0). -define(SOCKET_OPT_TCP_MAXSEG, 1). +-define(SOCKET_OPT_TCP_NODELAY, 2). + +-define(SOCKET_OPT_SCTP_AUTOCLOSE, 7). +-define(SOCKET_OPT_SCTP_NODELAY, 22). -define(SOCKET_SHUTDOWN_HOW_READ, 0). -define(SOCKET_SHUTDOWN_HOW_WRITE, 1). @@ -1654,6 +1659,15 @@ enc_setopt_value(tcp = L, Opt, V, _D, _T, _P) -> enc_setopt_value(udp = L, Opt, _V, _D, _T, _P) -> not_supported({L, Opt}); +enc_setopt_value(sctp, autoclose, V, _D, _T, P) + when is_integer(V) andalso + (P =:= sctp) -> + V; +enc_setopt_value(sctp, nodelay, V, _D, _T, P) + when is_boolean(V) andalso + (P =:= sctp) -> + V; + enc_setopt_value(L, Opt, V, _, _, _) when is_integer(L) andalso is_integer(Opt) andalso is_binary(V) -> V. @@ -1920,8 +1934,8 @@ enc_sockopt_key(tcp, congestion = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_TCP_CONGESTION; enc_sockopt_key(tcp, maxseg = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_TCP_MAXSEG; -enc_sockopt_key(tcp, nodelay = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); +enc_sockopt_key(tcp, nodelay = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_TCP_NODELAY; enc_sockopt_key(tcp, UnknownOpt, _Dir, _D, _T, _P) -> unknown(UnknownOpt); @@ -1930,6 +1944,10 @@ enc_sockopt_key(udp, UnknownOpt, _Dir, _D, _T, _P) -> unknown(UnknownOpt); %% SCTP socket options +enc_sockopt_key(sctp, autoclose = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SCTP_AUTOCLOSE; +enc_sockopt_key(sctp, nodelay = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SCTP_NODELAY; enc_sockopt_key(sctp, UnknownOpt, _Dir, _D, _T, _P) -> unknown(UnknownOpt); -- cgit v1.2.3 From d6b051d9b09aeba00d1bbd0d448dde6e551c4442 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 8 May 2018 12:36:35 +0200 Subject: [socket-nif] More setopt - udp Add setopt for one udp option: cork. This is not platform independent. Aas fas as I know, it only works on linux, so for now this serves as a placeholder. --- erts/preloaded/src/socket.erl | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 1c811fa11a..4a9de0dc9e 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -270,9 +270,8 @@ maxseg | nodelay. --type udp_socket_option() :: checksum | - maxdgram | - recvspace. +-type udp_socket_option() :: cork. + -type sctp_socket_option() :: adaption_layer | associnfo | @@ -444,6 +443,8 @@ -define(SOCKET_OPT_TCP_MAXSEG, 1). -define(SOCKET_OPT_TCP_NODELAY, 2). +-define(SOCKET_OPT_UDP_CORK, 1). + -define(SOCKET_OPT_SCTP_AUTOCLOSE, 7). -define(SOCKET_OPT_SCTP_NODELAY, 22). @@ -1638,7 +1639,7 @@ enc_setopt_value(ipv6, hoplimit, V, _D, T, _P) enc_setopt_value(ipv6 = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); -enc_setopt_value(tcp, congetsion, V, _D, T, P) +enc_setopt_value(tcp, congestion, V, _D, T, P) when is_list(V) andalso (T =:= stream) andalso (P =:= tcp) -> @@ -1656,6 +1657,11 @@ enc_setopt_value(tcp, nodelay, V, _D, T, P) enc_setopt_value(tcp = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); +enc_setopt_value(udp, cork, V, _D, T, P) + when is_boolean(V) andalso + (T =:= dgram) andalso + (P =:= udp) -> + V; enc_setopt_value(udp = L, Opt, _V, _D, _T, _P) -> not_supported({L, Opt}); @@ -1940,6 +1946,8 @@ enc_sockopt_key(tcp, UnknownOpt, _Dir, _D, _T, _P) -> unknown(UnknownOpt); %% UDP socket options +enc_sockopt_key(udp, cork = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_UDP_CORK; enc_sockopt_key(udp, UnknownOpt, _Dir, _D, _T, _P) -> unknown(UnknownOpt); -- cgit v1.2.3 From 7bc2d3e8618f561185694ae11afe4fc83b1f72f3 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 8 May 2018 12:58:56 +0200 Subject: [socket-nif] setopt of socket option reuseaddr --- erts/preloaded/src/socket.erl | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 4a9de0dc9e..6b245c41f0 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -183,16 +183,19 @@ priority | rcvbuf | rcvbufforce | - rcvlowat | sndlowat | - rcvtimeo | sndtimeo | + rcvlowat | + rcvtimeo | reuseaddr | reuseport | rxq_ovfl | + sndlowat | + sndtimeo | setfib | sndbuf | sndbufforce | timestamp | type. + %% Read-only options: %% mtu %% @@ -429,8 +432,9 @@ -define(SOCKET_OPT_OTP_DEBUG, 0). -define(SOCKET_OPT_OTP_IOW, 1). --define(SOCKET_OPT_SOCK_KEEPALIVE, 0). --define(SOCKET_OPT_SOCK_LINGER, 1). +-define(SOCKET_OPT_SOCK_KEEPALIVE, 9). +-define(SOCKET_OPT_SOCK_LINGER, 10). +-define(SOCKET_OPT_SOCK_REUSEADDR, 21). -define(SOCKET_OPT_IP_RECVTOS, 0). -define(SOCKET_OPT_IP_ROUTER_ALERT, 1). @@ -1611,6 +1615,8 @@ enc_setopt_value(socket, linger, abort, D, T, P) -> enc_setopt_value(socket, linger, {OnOff, Secs} = V, _D, _T, _P) when is_boolean(OnOff) andalso is_integer(Secs) andalso (Secs >= 0) -> V; +enc_setopt_value(socket, reuseaddr, V, _D, _T, _P) when is_boolean(V) -> + V; enc_setopt_value(socket = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); @@ -1826,8 +1832,8 @@ enc_sockopt_key(socket, rcvlowat = Opt, _Dir, _D, _T, _P) -> not_supported(Opt); enc_sockopt_key(socket, rcvtimeo = Opt, _Dir, _D, _T, _P) -> not_supported(Opt); -enc_sockopt_key(socket, reuseaddr = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); +enc_sockopt_key(socket, reuseaddr = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SOCK_REUSEADDR; enc_sockopt_key(socket, reuseport = Opt, _Dir, D, _T, _P) when ((D =:= inet) orelse (D =:= inet6)) -> not_supported(Opt); -- cgit v1.2.3 From 56641fca11c50306c6c62266844f7828e325ae7d Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 8 May 2018 15:26:41 +0200 Subject: [socket-nif] setopt of socket option priority --- erts/preloaded/src/socket.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 6b245c41f0..4c903bb759 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -434,6 +434,7 @@ -define(SOCKET_OPT_SOCK_KEEPALIVE, 9). -define(SOCKET_OPT_SOCK_LINGER, 10). +-define(SOCKET_OPT_SOCK_PRIORITY, 16). -define(SOCKET_OPT_SOCK_REUSEADDR, 21). -define(SOCKET_OPT_IP_RECVTOS, 0). @@ -1615,6 +1616,8 @@ enc_setopt_value(socket, linger, abort, D, T, P) -> enc_setopt_value(socket, linger, {OnOff, Secs} = V, _D, _T, _P) when is_boolean(OnOff) andalso is_integer(Secs) andalso (Secs >= 0) -> V; +enc_setopt_value(socket, priority, V, _D, _T, _P) when is_integer(V) -> + V; enc_setopt_value(socket, reuseaddr, V, _D, _T, _P) when is_boolean(V) -> V; enc_setopt_value(socket = L, Opt, V, _D, _T, _P) -> @@ -1821,8 +1824,8 @@ enc_sockopt_key(socket, peek_off = Opt, _Dir, local = _D, _T, _P) -> not_supported(Opt); enc_sockopt_key(socket, peek_cred = Opt, get = _Dir, _D, _T, _P) -> not_supported(Opt); -enc_sockopt_key(socket, priority = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); +enc_sockopt_key(socket, priority = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SOCK_PRIORITY; enc_sockopt_key(socket, rcvbuf = Opt, _Dir, _D, _T, _P) -> not_supported(Opt); enc_sockopt_key(socket, rcvbufforce = Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 3d719906dd0ad8d07547c3a20a2190a416dda364 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 8 May 2018 15:40:07 +0200 Subject: [socket-nif] setopt of socket option dontroute --- erts/preloaded/src/socket.erl | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 4c903bb759..f1b5623362 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -432,15 +432,16 @@ -define(SOCKET_OPT_OTP_DEBUG, 0). -define(SOCKET_OPT_OTP_IOW, 1). +-define(SOCKET_OPT_SOCK_DONTROUTE, 7). -define(SOCKET_OPT_SOCK_KEEPALIVE, 9). -define(SOCKET_OPT_SOCK_LINGER, 10). -define(SOCKET_OPT_SOCK_PRIORITY, 16). -define(SOCKET_OPT_SOCK_REUSEADDR, 21). --define(SOCKET_OPT_IP_RECVTOS, 0). --define(SOCKET_OPT_IP_ROUTER_ALERT, 1). --define(SOCKET_OPT_IP_TOS, 2). --define(SOCKET_OPT_IP_TTL, 3). +-define(SOCKET_OPT_IP_RECVTOS, 25). +-define(SOCKET_OPT_IP_ROUTER_ALERT, 28). +-define(SOCKET_OPT_IP_TOS, 30). +-define(SOCKET_OPT_IP_TTL, 32). -define(SOCKET_OPT_IPV6_HOPLIMIT, 12). @@ -1803,8 +1804,8 @@ enc_sockopt_key(socket, busy_poll = Opt, _Dir, _D, _T, _P) -> not_supported(Opt); enc_sockopt_key(socket, debug = Opt, _Dir, _D, _T, _P) -> not_supported(Opt); -enc_sockopt_key(socket, dontroute = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); +enc_sockopt_key(socket, dontroute = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SOCK_DONTROUTE; enc_sockopt_key(socket, error = Opt, get = _Dir, _D, _T, _P) -> not_supported(Opt); %% This is only for connection-oriented sockets, but who are those? -- cgit v1.2.3 From 0f2b8a76edaa462fca388a55eaf449a36296820a Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 8 May 2018 15:45:48 +0200 Subject: [socket-nif] setopt of socket option broadcast --- erts/preloaded/src/socket.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index f1b5623362..f40fd81d36 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -432,6 +432,7 @@ -define(SOCKET_OPT_OTP_DEBUG, 0). -define(SOCKET_OPT_OTP_IOW, 1). +-define(SOCKET_OPT_SOCK_BROADCAST, 4). -define(SOCKET_OPT_SOCK_DONTROUTE, 7). -define(SOCKET_OPT_SOCK_KEEPALIVE, 9). -define(SOCKET_OPT_SOCK_LINGER, 10). @@ -1610,6 +1611,8 @@ enc_setopt_value(otp, iow, V, _, _, _) when is_boolean(V) -> enc_setopt_value(otp = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); +enc_setopt_value(socket, broadcast, V, _D, _T, _P) when is_boolean(V) -> + V; enc_setopt_value(socket, keepalive, V, _D, _T, _P) when is_boolean(V) -> V; enc_setopt_value(socket, linger, abort, D, T, P) -> @@ -1798,8 +1801,8 @@ enc_sockopt_key(socket, acceptfilter = Opt, _Dir, _D, _T, _P) -> %% So, we let the implementation decide. enc_sockopt_key(socket, bindtodevide = Opt, _Dir, _D, _T, _P) -> not_supported(Opt); -enc_sockopt_key(socket, broadcast = Opt, _Dir, _D, dgram = _T, _P) -> - not_supported(Opt); +enc_sockopt_key(socket, broadcast = _Opt, _Dir, _D, dgram = _T, _P) -> + ?SOCKET_OPT_SOCK_BROADCAST; enc_sockopt_key(socket, busy_poll = Opt, _Dir, _D, _T, _P) -> not_supported(Opt); enc_sockopt_key(socket, debug = Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 83dd8317ea3534fb9353f0d5159a0b1cb485f38c Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 8 May 2018 16:49:58 +0200 Subject: [socket-nif] setopt for socket option(s) rcvbuf and sndnuf --- erts/preloaded/src/socket.erl | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index f40fd81d36..7b53f271ef 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -199,12 +199,14 @@ %% Read-only options: %% mtu %% +%% Options only valid on FreeBSD?: +%% dontfrag %% Options only valid for RAW sockets: -%% nodefrag +%% nodefrag (linux only?) -type ip_socket_option() :: add_membership | add_source_membership | block_source | - dont_frag | + dontfrag | drop_membership | drop_source_membership | freebind | @@ -437,7 +439,9 @@ -define(SOCKET_OPT_SOCK_KEEPALIVE, 9). -define(SOCKET_OPT_SOCK_LINGER, 10). -define(SOCKET_OPT_SOCK_PRIORITY, 16). +-define(SOCKET_OPT_SOCK_RCVBUF, 17). -define(SOCKET_OPT_SOCK_REUSEADDR, 21). +-define(SOCKET_OPT_SOCK_SNDBUF, 27). -define(SOCKET_OPT_IP_RECVTOS, 25). -define(SOCKET_OPT_IP_ROUTER_ALERT, 28). @@ -1600,9 +1604,10 @@ enc_setopt_key(Level, Opt, Domain, Type, Protocol) -> %% +++ Encode setopt value +++ %% -%% For the most part this function does *not* do an actually encode, -%% it simply validates the value type. But in some cases it actually -%% encodes the value into an more manageable type. +%% For the most part this function does *not* do an actual encode, +%% it simply validates the value type. But in some cases it will +%% encode the value into an more "manageable" type. +%% It also handles "aliases" (see linger). enc_setopt_value(otp, debug, V, _, _, _) when is_boolean(V) -> V; @@ -1622,8 +1627,12 @@ enc_setopt_value(socket, linger, {OnOff, Secs} = V, _D, _T, _P) V; enc_setopt_value(socket, priority, V, _D, _T, _P) when is_integer(V) -> V; +enc_setopt_value(socket, rcvbuf, V, _D, _T, _P) when is_integer(V) -> + V; enc_setopt_value(socket, reuseaddr, V, _D, _T, _P) when is_boolean(V) -> V; +enc_setopt_value(socket, sndbuf, V, _D, _T, _P) when is_integer(V) -> + V; enc_setopt_value(socket = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); @@ -1830,8 +1839,8 @@ enc_sockopt_key(socket, peek_cred = Opt, get = _Dir, _D, _T, _P) -> not_supported(Opt); enc_sockopt_key(socket, priority = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SOCK_PRIORITY; -enc_sockopt_key(socket, rcvbuf = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); +enc_sockopt_key(socket, rcvbuf = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SOCK_RCVBUF; enc_sockopt_key(socket, rcvbufforce = Opt, _Dir, _D, _T, _P) -> not_supported(Opt); %% May not work on linux. @@ -1848,8 +1857,8 @@ enc_sockopt_key(socket, rxq_ovfl = Opt, _Dir, _D, _T, _P) -> not_supported(Opt); enc_sockopt_key(socket, setfib = Opt, set = _Dir, _D, _T, _P) -> not_supported(Opt); -enc_sockopt_key(socket, sndbuf = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); +enc_sockopt_key(socket, sndbuf = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SOCK_SNDBUF; enc_sockopt_key(socket, sndbufforce = Opt, _Dir, _D, _T, _P) -> not_supported(Opt); %% Not changeable on linux. -- cgit v1.2.3 From 24bcbd2040fad723648e25af87cd848da8aa27bc Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 17 May 2018 14:22:52 +0200 Subject: [socket-nif] getopt partially implemented There are still many options not implemented (just as for setopt), but this will have to do for now... --- erts/preloaded/src/socket.erl | 71 ++++++++++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 17 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 7b53f271ef..f8aa75bf8f 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -272,6 +272,7 @@ v6only. -type tcp_socket_option() :: congestion | + cork | maxseg | nodelay. @@ -451,10 +452,11 @@ -define(SOCKET_OPT_IPV6_HOPLIMIT, 12). -define(SOCKET_OPT_TCP_CONGESTION, 0). --define(SOCKET_OPT_TCP_MAXSEG, 1). --define(SOCKET_OPT_TCP_NODELAY, 2). +-define(SOCKET_OPT_TCP_CORK, 2). +-define(SOCKET_OPT_TCP_MAXSEG, 3). +-define(SOCKET_OPT_TCP_NODELAY, 4). --define(SOCKET_OPT_UDP_CORK, 1). +-define(SOCKET_OPT_UDP_CORK, 0). -define(SOCKET_OPT_SCTP_AUTOCLOSE, 7). -define(SOCKET_OPT_SCTP_NODELAY, 22). @@ -1370,6 +1372,10 @@ setopt(#socket{info = Info, ref = SockRef}, Level, Key, Value) -> %% If its an "invalid" option, we should not crash but return some %% useful error... %% +%% When specifying level as an integer, and therefor using "native mode", +%% we should make it possible to specify common types instead of the +%% value size. Example: int | bool | {string, pos_integer()} | non_neg_integer() +%% -spec getopt(Socket, Level, Key) -> {ok, Value} | {error, Reason} when Socket :: socket(), @@ -1412,7 +1418,15 @@ setopt(#socket{info = Info, ref = SockRef}, Level, Key, Value) -> Level :: sctp, Key :: sctp_socket_option(), Value :: term(), - Reason :: term(). + Reason :: term() + ; (Socket, Level, Key) -> ok | {ok, Value} | {error, Reason} when + Socket :: socket(), + Level :: integer(), + Key :: {NativeOpt, ValueSize}, + NativeOpt :: integer(), + ValueSize :: non_neg_integer(), + Value :: term(), + Reason :: term(). getopt(#socket{info = Info, ref = SockRef}, Level, Key) -> try @@ -1425,6 +1439,8 @@ getopt(#socket{info = Info, ref = SockRef}, Level, Key) -> %% We may need to decode the value (for the same reason %% we needed to encode the value for setopt). case nif_getopt(SockRef, EIsEncoded, ELevel, EKey) of + ok -> + ok; {ok, EVal} -> Val = dec_getopt_value(Level, Key, EVal, Domain, Type, Protocol), @@ -1716,10 +1732,14 @@ enc_getopt_key(Level, Opt, Domain, Type, Protocol) -> %% +++ Decode getopt value +++ +%% +%% For the most part, we simply let the value pass through, but for some +%% values we do an actual decode. +%% -%% We should ...really... do something with the domain, type and protocol args... -dec_getopt_value(otp, debug, B, _, _, _) when is_boolean(B) -> - B. +%% Let the user deal with this... +dec_getopt_value(_L, _Opt, V, _D, _T, _P) -> + V. @@ -1731,64 +1751,74 @@ dec_getopt_value(otp, debug, B, _, _, _) when is_boolean(B) -> Direction, Domain, Type, Protocol) -> non_neg_integer() when Level :: otp, - Opt :: otp_socket_option(), Direction :: set | get, + Opt :: otp_socket_option(), Domain :: domain(), Type :: type(), Protocol :: protocol() ; (Level, Direction, Opt, Domain, Type, Protocol) -> non_neg_integer() when Level :: socket, - Opt :: socket_option(), Direction :: set | get, + Opt :: socket_option(), Domain :: domain(), Type :: type(), Protocol :: protocol() ; (Level, Direction, Opt, Domain, Type, Protocol) -> non_neg_integer() when Level :: ip, - Opt :: ip_socket_option(), Direction :: set | get, + Opt :: ip_socket_option(), Domain :: domain(), Type :: type(), Protocol :: protocol() ; (Level, Direction, Opt, Domain, Type, Protocol) -> non_neg_integer() when Level :: ipv6, - Opt :: ipv6_socket_option(), Direction :: set | get, + Opt :: ipv6_socket_option(), Domain :: domain(), Type :: type(), Protocol :: protocol() ; (Level, Direction, Opt, Domain, Type, Protocol) -> non_neg_integer() when Level :: tcp, - Opt :: tcp_socket_option(), Direction :: set | get, + Opt :: tcp_socket_option(), Domain :: domain(), Type :: type(), Protocol :: protocol() ; (Level, Direction, Opt, Domain, Type, Protocol) -> non_neg_integer() when Level :: udp, - Opt :: udp_socket_option(), Direction :: set | get, + Opt :: udp_socket_option(), Domain :: domain(), Type :: type(), Protocol :: protocol() ; (Level, Direction, Opt, Domain, Type, Protocol) -> non_neg_integer() when Level :: sctp, - Opt :: sctp_socket_option(), Direction :: set | get, + Opt :: sctp_socket_option(), Domain :: domain(), Type :: type(), Protocol :: protocol() ; (Level, Direction, Opt, Domain, Type, Protocol) -> non_neg_integer() when Level :: integer(), + Direction :: set, Opt :: integer(), - Direction :: set | get, + Domain :: domain(), + Type :: type(), + Protocol :: protocol() + ; (Level, Direction, Opt, + Domain, Type, Protocol) -> non_neg_integer() when + Level :: integer(), + Direction :: get, + Opt :: {NativeOpt, ValueSize}, + NativeOpt :: integer(), + ValueSize :: non_neg_integer(), Domain :: domain(), Type :: type(), Protocol :: protocol(). @@ -1960,6 +1990,8 @@ enc_sockopt_key(ipv6, UnknownOpt, _Dir, _D, _T, _P) -> %% but they are difficult to get portable... enc_sockopt_key(tcp, congestion = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_TCP_CONGESTION; +enc_sockopt_key(tcp, cork = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); enc_sockopt_key(tcp, maxseg = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_TCP_MAXSEG; enc_sockopt_key(tcp, nodelay = _Opt, _Dir, _D, _T, _P) -> @@ -1981,10 +2013,15 @@ enc_sockopt_key(sctp, nodelay = _Opt, _Dir, _D, _T, _P) -> enc_sockopt_key(sctp, UnknownOpt, _Dir, _D, _T, _P) -> unknown(UnknownOpt); -%% +++ Plain socket options +++ -enc_sockopt_key(Level, Opt, _Dir, _D, _T, _P) +%% +++ "Native" socket options +++ +enc_sockopt_key(Level, Opt, set = _Dir, _D, _T, _P) when is_integer(Level) andalso is_integer(Opt) -> Opt; +enc_sockopt_key(Level, {NativeOpt, ValueSize} = Opt, get = _Dir, _D, _T, _P) + when is_integer(Level) andalso + is_integer(NativeOpt) andalso + is_integer(ValueSize) andalso (ValueSize >= 0) -> + Opt; enc_sockopt_key(Level, Opt, _Dir, _Domain, _Type, _Protocol) -> unknown({Level, Opt}). -- cgit v1.2.3 From 4f165a244cb23405ed7f607836098f180f1be08b Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 17 May 2018 15:21:59 +0200 Subject: [socket-nif] More getopt Added guards and type specs for native getopt. --- erts/preloaded/src/socket.erl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index f8aa75bf8f..a67a019b80 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -1424,7 +1424,7 @@ setopt(#socket{info = Info, ref = SockRef}, Level, Key, Value) -> Level :: integer(), Key :: {NativeOpt, ValueSize}, NativeOpt :: integer(), - ValueSize :: non_neg_integer(), + ValueSize :: int | bool | non_neg_integer(), Value :: term(), Reason :: term(). @@ -1437,7 +1437,7 @@ getopt(#socket{info = Info, ref = SockRef}, Level, Key) -> {EIsEncoded, ELevel} = enc_getopt_level(Level), EKey = enc_getopt_key(Level, Key, Domain, Type, Protocol), %% We may need to decode the value (for the same reason - %% we needed to encode the value for setopt). + %% we (may have) needed to encode the value for setopt). case nif_getopt(SockRef, EIsEncoded, ELevel, EKey) of ok -> ok; @@ -2020,7 +2020,8 @@ enc_sockopt_key(Level, Opt, set = _Dir, _D, _T, _P) enc_sockopt_key(Level, {NativeOpt, ValueSize} = Opt, get = _Dir, _D, _T, _P) when is_integer(Level) andalso is_integer(NativeOpt) andalso - is_integer(ValueSize) andalso (ValueSize >= 0) -> + ((is_integer(ValueSize) andalso (ValueSize >= 0)) orelse + ((ValueSize =:= int) orelse (ValueSize =:= bool))) -> Opt; enc_sockopt_key(Level, Opt, _Dir, _Domain, _Type, _Protocol) -> -- cgit v1.2.3 From 63338250778d2caad08aa3180b372e5260f22aa7 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 31 May 2018 10:58:19 +0200 Subject: [socket-nif-doc] Add preliminary doc for socket The doc now builds. Had to update the code (spec and types) to match. Though, te result is less then stellar. OTP-14831 --- erts/preloaded/src/socket.erl | 152 ++++++++++++++++++++---------------------- 1 file changed, 71 insertions(+), 81 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index a67a019b80..2942f26505 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -30,13 +30,13 @@ -export([ open/2, open/3, open/4, - bind/2, bind/3, - connect/3, + bind/2, + connect/2, connect/3, listen/1, listen/2, accept/1, accept/2, send/2, send/3, send/4, - sendto/5, + sendto/4, sendto/5, %% sendmsg/4, %% writev/4, OR SENDV? It will be strange for recv then: recvv (instead of readv) @@ -125,6 +125,8 @@ %% If we do we may need to include the family (domain) in the %% map (as the native type do. See struct sockaddr_in6). %% +-type in_sockaddr() :: in4_sockaddr() | in6_sockaddr(). + -record(in4_sockaddr, {port = 0 :: port_number(), addr = any :: any | loopback | ip4_address()}). -type in4_sockaddr() :: #in4_sockaddr{}. @@ -134,8 +136,6 @@ scope_id = 0 :: in6_scope_id()}). -type in6_sockaddr() :: #in6_sockaddr{}. --type in_sockaddr() :: in4_sockaddr() | in6_sockaddr(). - -type port_number() :: 0..65535. %% otp - The option is internal to our (OTP) imeplementation. @@ -369,7 +369,7 @@ %% Optional address %% On an unconnected socket this is used to specify the target %% address for a datagram. - %% For a connected socket, this field should be specified []. + %% For a connected socket, this field should be specifiedset to []. name :: list(), %% Scatter/gather array @@ -495,7 +495,8 @@ on_load(Path, Extra) when is_list(Path) andalso is_map(Extra) -> on_load(true, _Path, _Extra) -> ok; on_load(false, Path, Extra) -> - ok = erlang:load_nif(Path, maps:put(timestamp, formated_timestamp(), Extra)). + %% ok = erlang:load_nif(Path, maps:put(timestamp, formated_timestamp(), Extra)). + ok = erlang:load_nif(Path, Extra). @@ -582,7 +583,7 @@ default_protocol(Protocol, _) -> Protocol. -spec bind(Socket, FileOrAddr) -> ok | {error, Reason} when Socket :: socket(), - FileOrAddr :: binary() | string() | ip_address() | any | loopback, + FileOrAddr :: binary() | string() | in_sockaddr() | any | loopback, Reason :: term(). bind(Socket, File) when is_binary(File) -> @@ -602,26 +603,11 @@ bind(Socket, File) when is_list(File) andalso (File =/= []) -> true -> {error, einval} end; -bind(Socket, Addr) when is_tuple(Addr) orelse - (Addr =:= any) orelse - (Addr =:= loopback) -> - bind(Socket, Addr, 0). - - --spec bind(Socket, Address, Port) -> ok | {ok, NewPort} | {error, Reason} when - Socket :: socket(), - Address :: ip_address() | any | loopback, - Port :: port_number(), - NewPort :: port_number(), - Reason :: term(). - -%% Shall we keep info about domain so that we can verify address? -bind(#socket{ref = SockRef}, Addr, Port) - when (is_tuple(Addr) andalso - ((size(Addr) =:= 4) orelse (size(Addr) =:= 8))) orelse - ((Addr =:= any) orelse (Addr =:= loopback)) andalso - (is_integer(Port) andalso (Port >= 0)) -> - nif_bind(SockRef, {Addr, Port}). +bind(#socket{ref = SockRef} = _Socket, SockAddr) + when is_record(SockAddr, in4_sockaddr) orelse + is_record(SockAddr, in6_sockaddr) orelse + (SockAddr =:= any) orelse (SockAddr =:= loopback) -> + nif_bind(SockRef, SockAddr). @@ -630,32 +616,29 @@ bind(#socket{ref = SockRef}, Addr, Port) %% connect - initiate a connection on a socket %% --spec connect(Socket, Addr, Port) -> ok | {error, Reason} when - Socket :: socket(), - Addr :: ip_address(), - Port :: port_number(), - Reason :: term(). +-spec connect(Socket, SockAddr) -> ok | {error, Reason} when + Socket :: socket(), + SockAddr :: in_sockaddr(), + Reason :: term(). -connect(Socket, Addr, Port) -> - connect(Socket, Addr, Port, infinity). +connect(Socket, SockAddr) -> + connect(Socket, SockAddr, infinity). --spec connect(Socket, Addr, Port, Timeout) -> ok | {error, Reason} when - Socket :: socket(), - Addr :: ip_address(), - Port :: port_number(), - Timeout :: timeout(), - Reason :: term(). +-spec connect(Socket, SockAddr, Timeout) -> ok | {error, Reason} when + Socket :: socket(), + SockAddr :: in_sockaddr(), + Timeout :: timeout(), + Reason :: term(). -connect(_Socket, _Addr, _Port, Timeout) +connect(_Socket, _SockAddr, Timeout) when (is_integer(Timeout) andalso (Timeout =< 0)) -> {error, timeout}; -connect(#socket{ref = SockRef}, Addr, Port, Timeout) - when (is_tuple(Addr) andalso - ((size(Addr) =:= 4) orelse (size(Addr) =:= 8))) andalso - (is_integer(Port) andalso (Port >= 0)) andalso +connect(#socket{ref = SockRef}, SockAddr, Timeout) + when (is_record(SockAddr, in4_sockaddr) orelse + is_record(SockAddr, in6_sockaddr)) andalso ((Timeout =:= infinity) orelse is_integer(Timeout)) -> TS = timestamp(Timeout), - case nif_connect(SockRef, Addr, Port) of + case nif_connect(SockRef, SockAddr) of ok -> %% Connected! ok; @@ -680,14 +663,18 @@ connect(#socket{ref = SockRef}, Addr, Port, Timeout) %% listen - listen for connections on a socket %% --spec listen(Socket, Backlog) -> ok | {error, Reason} when +-spec listen(Socket) -> ok | {error, Reason} when Socket :: socket(), - Backlog :: pos_integer(), Reason :: term(). listen(Socket) -> listen(Socket, ?SOCKET_LISTEN_BACKLOG_DEFAULT). +-spec listen(Socket, Backlog) -> ok | {error, Reason} when + Socket :: socket(), + Backlog :: pos_integer(), + Reason :: term(). + listen(#socket{ref = SockRef}, Backlog) when (is_integer(Backlog) andalso (Backlog >= 0)) -> nif_listen(SockRef, Backlog). @@ -700,15 +687,20 @@ listen(#socket{ref = SockRef}, Backlog) %% accept, accept4 - accept a connection on a socket %% --spec accept(LSocket, Timeout) -> {ok, Socket} | {error, Reason} when +-spec accept(LSocket) -> {ok, Socket} | {error, Reason} when LSocket :: socket(), - Timeout :: timeout(), Socket :: socket(), Reason :: term(). accept(Socket) -> accept(Socket, ?SOCKET_ACCEPT_TIMEOUT_DEFAULT). +-spec accept(LSocket, Timeout) -> {ok, Socket} | {error, Reason} when + LSocket :: socket(), + Timeout :: timeout(), + Socket :: socket(), + Reason :: term(). + %% Do we really need this optimization? accept(_, Timeout) when is_integer(Timeout) andalso (Timeout =< 0) -> {error, timeout}; @@ -824,35 +816,35 @@ do_send(SockRef, Data, EFlags, Timeout) -> %% --------------------------------------------------------------------------- %% -sendto(Socket, Data, Flags, DestAddr, DestPort) -> - sendto(Socket, Data, Flags, DestAddr, DestPort, ?SOCKET_SENDTO_TIMEOUT_DEFAULT). +sendto(Socket, Data, Flags, DestSockAddr) -> + sendto(Socket, Data, Flags, DestSockAddr, ?SOCKET_SENDTO_TIMEOUT_DEFAULT). --spec sendto(Socket, Data, Flags, DestAddr, DestPort, Timeout) -> +-spec sendto(Socket, Data, Flags, DestSockAddr, Timeout) -> ok | {error, Reason} when - Socket :: socket(), - Data :: binary(), - Flags :: send_flags(), - DestAddr :: null | ip_address(), - DestPort :: port_number(), - Timeout :: timeout(), - Reason :: term(). - -sendto(Socket, Data, Flags, DestAddr, DestPort, Timeout) when is_list(Data) -> + Socket :: socket(), + Data :: binary(), + Flags :: send_flags(), + DestSockAddr :: null | in_sockaddr(), + Timeout :: timeout(), + Reason :: term(). + +sendto(Socket, Data, Flags, DestSockAddr, Timeout) when is_list(Data) -> Bin = erlang:list_to_binary(Data), - sendto(Socket, Bin, Flags, DestAddr, DestPort, Timeout); -sendto(#socket{ref = SockRef}, Data, Flags, DestAddr, DestPort, Timeout) + sendto(Socket, Bin, Flags, DestSockAddr, Timeout); +sendto(#socket{ref = SockRef}, Data, Flags, DestSockAddr, Timeout) when is_binary(Data) andalso is_list(Flags) andalso - (is_tuple(DestAddr) orelse (DestAddr =:= null)) andalso - is_integer(DestPort) andalso + (is_record(DestSockAddr, in4_sockaddr) orelse + is_record(DestSockAddr, in6_sockaddr) orelse + (DestSockAddr =:= null)) andalso (is_integer(Timeout) orelse (Timeout =:= infinity)) -> EFlags = enc_send_flags(Flags), - do_sendto(SockRef, Data, EFlags, DestAddr, DestPort, Timeout). + do_sendto(SockRef, Data, EFlags, DestSockAddr, Timeout). -do_sendto(SockRef, Data, EFlags, DestAddr, DestPort, Timeout) -> +do_sendto(SockRef, Data, EFlags, DestSockAddr, Timeout) -> TS = timestamp(Timeout), SendRef = make_ref(), - case nif_sendto(SockRef, SendRef, Data, EFlags, DestAddr, DestPort) of + case nif_sendto(SockRef, SendRef, Data, EFlags, DestSockAddr) of ok -> %% We are done ok; @@ -862,10 +854,10 @@ do_sendto(SockRef, Data, EFlags, DestAddr, DestPort, Timeout) -> receive {select, SockRef, SendRef, ready_output} when (Written > 0) -> <<_:Written/binary, Rest/binary>> = Data, - do_sendto(SockRef, Rest, EFlags, DestAddr, DestPort, + do_sendto(SockRef, Rest, EFlags, DestSockAddr, next_timeout(TS, Timeout)); {select, SockRef, SendRef, ready_output} -> - do_sendto(SockRef, Data, EFlags, DestAddr, DestPort, + do_sendto(SockRef, Data, EFlags, DestSockAddr, next_timeout(TS, Timeout)); {nif_abort, SendRef, Reason} -> @@ -880,7 +872,7 @@ do_sendto(SockRef, Data, EFlags, DestAddr, DestPort, Timeout) -> {error, eagain} -> receive {select, SockRef, SendRef, ready_output} -> - do_sendto(SockRef, Data, EFlags, DestAddr, DestPort, + do_sendto(SockRef, Data, EFlags, DestSockAddr, next_timeout(TS, Timeout)) after Timeout -> nif_cancel(SockRef, sendto, SendRef), @@ -1158,13 +1150,12 @@ recvfrom(Socket, BufSz, Flags) when is_list(Flags) -> recvfrom(Socket, BufSz, Timeout) -> recvfrom(Socket, BufSz, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout). --spec recvfrom(Socket, BufSz, Flags, Timeout) -> {ok, {SrcDomain, Source, Data}} | {error, Reason} when +-spec recvfrom(Socket, BufSz, Flags, Timeout) -> {ok, {Source, Data}} | {error, Reason} when Socket :: socket(), BufSz :: non_neg_integer(), Flags :: recv_flags(), Timeout :: timeout(), - SrcDomain :: domain() | undefined, - Source :: {ip_address(), port_number()} | string() | undefined, + Source :: in_sockaddr() | string() | undefined, Data :: binary(), Reason :: term(). @@ -1179,7 +1170,7 @@ do_recvfrom(SockRef, BufSz, EFlags, Timeout) -> TS = timestamp(Timeout), RecvRef = make_ref(), case nif_recvfrom(SockRef, RecvRef, BufSz, EFlags) of - {ok, {_Domain, _Source, _NewData}} = OK -> + {ok, {_Source, _NewData}} = OK -> OK; {error, eagain} -> @@ -1473,7 +1464,6 @@ link_if2idx(If) when is_list(If) -> nif_link_if2idx(If). - %% =========================================================================== %% %% link_idx2if - Mappings between network interface names and indexes: idx -> if @@ -2138,10 +2128,10 @@ nif_info() -> nif_open(_Domain, _Type, _Protocol, _Extra) -> erlang:error(badarg). -nif_bind(_SRef, _LocalAddr) -> +nif_bind(_SRef, _SockAddr) -> erlang:error(badarg). -nif_connect(_SRef, _Addr, _Port) -> +nif_connect(_SRef, _SockAddr) -> erlang:error(badarg). nif_finalize_connection(_SRef) -> @@ -2156,7 +2146,7 @@ nif_accept(_SRef, _Ref) -> nif_send(_SockRef, _SendRef, _Data, _Flags) -> erlang:error(badarg). -nif_sendto(_SRef, _SendRef, _Data, _Flags, _Dest, _Port) -> +nif_sendto(_SRef, _SendRef, _Data, _Flags, _DestSockAddr) -> erlang:error(badarg). nif_recv(_SRef, _RecvRef, _Length, _Flags) -> -- cgit v1.2.3 From 9e0acc8f442549f1f5ee4271816cbfbeb25d8719 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 7 Jun 2018 11:03:27 +0200 Subject: [socket-nif-doc] Fixed socket type and function open The doc for type socket was missing (as it is opaque), so instead its replaced with a simple text referring to the functions open and accept. The open function missed a spec (for open/2), the text for default protocol needed some omrpvement and finally the Extra argument needed explaining. --- erts/preloaded/src/socket.erl | 54 ++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 24 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 2942f26505..932048ba75 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -518,6 +518,12 @@ info() -> %% open - create an endpoint for communication %% +-spec open(Domain, Type) -> {ok, Socket} | {error, Reason} when + Domain :: domain(), + Type :: type(), + Socket :: socket(), + Reason :: term(). + open(Domain, Type) -> open(Domain, Type, null). @@ -2044,30 +2050,30 @@ flush_select_msgs(LSRef, Ref) -> end. -formated_timestamp() -> - format_timestamp(os:timestamp()). - -format_timestamp(Now) -> - N2T = fun(N) -> calendar:now_to_local_time(N) end, - format_timestamp(Now, N2T, true). - -format_timestamp({_N1, _N2, N3} = N, N2T, true) -> - FormatExtra = ".~.2.0w", - ArgsExtra = [N3 div 10000], - format_timestamp(N, N2T, FormatExtra, ArgsExtra); -format_timestamp({_N1, _N2, _N3} = N, N2T, false) -> - FormatExtra = "", - ArgsExtra = [], - format_timestamp(N, N2T, FormatExtra, ArgsExtra). - -format_timestamp(N, N2T, FormatExtra, ArgsExtra) -> - {Date, Time} = N2T(N), - {YYYY,MM,DD} = Date, - {Hour,Min,Sec} = Time, - FormatDate = - io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w" ++ FormatExtra, - [YYYY, MM, DD, Hour, Min, Sec] ++ ArgsExtra), - lists:flatten(FormatDate). +%% formated_timestamp() -> +%% format_timestamp(os:timestamp()). + +%% format_timestamp(Now) -> +%% N2T = fun(N) -> calendar:now_to_local_time(N) end, +%% format_timestamp(Now, N2T, true). + +%% format_timestamp({_N1, _N2, N3} = N, N2T, true) -> +%% FormatExtra = ".~.2.0w", +%% ArgsExtra = [N3 div 10000], +%% format_timestamp(N, N2T, FormatExtra, ArgsExtra); +%% format_timestamp({_N1, _N2, _N3} = N, N2T, false) -> +%% FormatExtra = "", +%% ArgsExtra = [], +%% format_timestamp(N, N2T, FormatExtra, ArgsExtra). + +%% format_timestamp(N, N2T, FormatExtra, ArgsExtra) -> +%% {Date, Time} = N2T(N), +%% {YYYY,MM,DD} = Date, +%% {Hour,Min,Sec} = Time, +%% FormatDate = +%% io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w" ++ FormatExtra, +%% [YYYY, MM, DD, Hour, Min, Sec] ++ ArgsExtra), +%% lists:flatten(FormatDate). %% A timestamp in ms -- cgit v1.2.3 From 70b74f57a360c2b2a1edfe46c61d2ea170d73f91 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 7 Jun 2018 17:50:38 +0200 Subject: [socket-nif-doc] More polishing Also added (a very) temporary example. OTP-14831 --- erts/preloaded/src/socket.erl | 136 +++++++++++++++++++++++++++++------------- 1 file changed, 96 insertions(+), 40 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 932048ba75..772c733bc7 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -159,6 +159,7 @@ -type otp_socket_option() :: debug | iow | + controlling_process | rcvbuf | sndbuf. %% Shall we have special treatment of linger?? @@ -517,6 +518,63 @@ info() -> %% %% open - create an endpoint for communication %% +%% Extra: netns +%% +%% +%% +%% How do we handle the case when an fd has beem created (somehow) +%% and we shall create a socket "from it". +%% Can we figure out Domain, Type and Protocol from fd? +%% Yes we can: SO_DOMAIN, SO_PROTOCOL, SO_TYPE +%% +%% +%% +%% +%% +%% +%% Start a controller process here, *before* the nif_open call. +%% If that call is successful, update with owner process (controlling +%% process) and SockRef. If the open fails, kill the process. +%% "Register" the process on success: +%% +%% nif_register(SockRef, self()). +%% +%% The nif sets up a monitor to this process, and if it dies the socket +%% is closed. It is also used if someone wants to monitor the socket. +%% +%% We therefor need monitor function(s): +%% +%% socket:monitor(Socket) +%% socket:demonitor(Socket) +%% +%% These are basically used to monitor the controller process. +%% +%% +%% + +%% -spec open(FD) -> {ok, Socket} | {error, Reason} when +%% Socket :: socket(), +%% Reason :: term(). + +%% open(FD) -> +%% try +%% begin +%% case nif_open(FD) of +%% {ok, {SockRef, Domain, Type, Protocol}} -> +%% SocketInfo = #{domain => Domain, +%% type => Type, +%% protocol => Protocol}, +%% Socket = #socket{info = SocketInfo, +%% ref = SockRef}, +%% {ok, Socket}; +%% {error, _} = ERROR -> +%% ERROR +%% end +%% end +%% catch +%% _:_ -> % This must be improved!! +%% {error, einval} +%% end. -spec open(Domain, Type) -> {ok, Socket} | {error, Reason} when Domain :: domain(), @@ -653,6 +711,20 @@ connect(#socket{ref = SockRef}, SockAddr, Timeout) NewTimeout = next_timeout(TS, Timeout), receive {select, SockRef, Ref, ready_output} -> + %% + %% + %% See open above!! + %% + %% * Here we should start and *register* the reader process + %% (This will cause the nif code to create a monitor to + %% the process) + %% * The reader is basically used to implement the active-X + %% feature! + %% * If the reader dies for whatever reason, then the socket + %% (resource) closes and the owner (controlling) process + %% is informed (closed message). + %% + %% nif_finalize_connection(SockRef) after NewTimeout -> nif_cancel(SockRef, connect, Ref), @@ -719,6 +791,16 @@ do_accept(LSockRef, SI, Timeout) -> AccRef = make_ref(), case nif_accept(LSockRef, AccRef) of {ok, SockRef} -> + %% + %% + %% * Here we should start and *register* the reader process + %% (This will cause the nif code to create a monitor to the process) + %% * The reader is basically used to implement the active-X feature! + %% * If the reader dies for whatever reason, then the socket (resource) + %% closes and the owner (controlling) process is informed (closed + %% message). + %% + %% SocketInfo = #{domain => maps:get(domain, SI), type => maps:get(type, SI), protocol => maps:get(protocol, SI)}, @@ -1297,46 +1379,32 @@ shutdown(#socket{ref = SockRef}, How) -> %% %% --spec setopt(Socket, Level, Opt, Value) -> ok | {error, Reason} when +-spec setopt(Socket, otp, otp_socket_option(), Value) -> ok | {error, Reason} when Socket :: socket(), - Level :: otp, - Opt :: otp_socket_option(), Value :: term(), Reason :: term() - ; (Socket, Level, Opt, Value) -> ok | {error, Reason} when + ; (Socket, socket, socket_option(), Value) -> ok | {error, Reason} when Socket :: socket(), - Level :: socket, - Opt :: socket_option(), Value :: term(), Reason :: term() - ; (Socket, Level, Opt, Value) -> ok | {error, Reason} when + ; (Socket, ip, ip_socket_option(), Value) -> ok | {error, Reason} when Socket :: socket(), - Level :: ip, - Opt :: ip_socket_option(), Value :: term(), Reason :: term() - ; (Socket, Level, Opt, Value) -> ok | {error, Reason} when + ; (Socket, ipv6, ipv6_socket_option(), Value) -> ok | {error, Reason} when Socket :: socket(), - Level :: ipv6, - Opt :: ipv6_socket_option(), Value :: term(), Reason :: term() - ; (Socket, Level, Opt, Value) -> ok | {error, Reason} when + ; (Socket, tcp, tcp_socket_option(), Value) -> ok | {error, Reason} when Socket :: socket(), - Level :: tcp, - Opt :: tcp_socket_option(), Value :: term(), Reason :: term() - ; (Socket, Level, Opt, Value) -> ok | {error, Reason} when + ; (Socket, udp, udp_socket_option(), Value) -> ok | {error, Reason} when Socket :: socket(), - Level :: udp, - Opt :: udp_socket_option(), Value :: term(), Reason :: term() - ; (Socket, Level, Opt, Value) -> ok | {error, Reason} when + ; (Socket, sctp, sctp_socket_option(), Value) -> ok | {error, Reason} when Socket :: socket(), - Level :: sctp, - Opt :: sctp_socket_option(), Value :: term(), Reason :: term(). @@ -1374,46 +1442,34 @@ setopt(#socket{info = Info, ref = SockRef}, Level, Key, Value) -> %% value size. Example: int | bool | {string, pos_integer()} | non_neg_integer() %% --spec getopt(Socket, Level, Key) -> {ok, Value} | {error, Reason} when +-spec getopt(Socket, otp, otp_socket_option()) -> {ok, Value} | {error, Reason} when Socket :: socket(), - Level :: otp, - Key :: otp_socket_option(), Value :: term(), Reason :: term() - ; (Socket, Level, Key) -> {ok, Value} | {error, Reason} when + ; (Socket, socket, socket_option()) -> {ok, Value} | {error, Reason} when Socket :: socket(), - Level :: socket, - Key :: socket_option(), Value :: term(), Reason :: term() - ; (Socket, Level, Key) -> {ok, Value} | {error, Reason} when + ; (Socket, ip, ip_socket_option()) -> {ok, Value} | {error, Reason} when Socket :: socket(), Level :: ip, Key :: ip_socket_option(), Value :: term(), Reason :: term() - ; (Socket, Level, Key) -> {ok, Value} | {error, Reason} when + ; (Socket, ipv6, ipv6_socket_option()) -> {ok, Value} | {error, Reason} when Socket :: socket(), - Level :: ipv6, - Key :: ipv6_socket_option(), Value :: term(), Reason :: term() - ; (Socket, Level, Key) -> {ok, Value} | {error, Reason} when + ; (Socket, tcp, tcp_socket_option()) -> {ok, Value} | {error, Reason} when Socket :: socket(), - Level :: tcp, - Key :: tcp_socket_option(), Value :: term(), Reason :: term() - ; (Socket, Level, Key) -> {ok, Value} | {error, Reason} when + ; (Socket, udp, udp_socket_option()) -> {ok, Value} | {error, Reason} when Socket :: socket(), - Level :: udp, - Key :: udp_socket_option(), Value :: term(), Reason :: term() - ; (Socket, Level, Key) -> {ok, Value} | {error, Reason} when + ; (Socket, sctp, sctp_socket_option()) -> {ok, Value} | {error, Reason} when Socket :: socket(), - Level :: sctp, - Key :: sctp_socket_option(), Value :: term(), Reason :: term() ; (Socket, Level, Key) -> ok | {ok, Value} | {error, Reason} when -- cgit v1.2.3 From 348677edc60a33bf31210a0ddfeb327e1be6f9c2 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 12 Jun 2018 16:57:53 +0200 Subject: [socket-nif] Fixed getopt function spec --- erts/preloaded/src/socket.erl | 2 -- 1 file changed, 2 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 772c733bc7..90fe5b9f36 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -1452,8 +1452,6 @@ setopt(#socket{info = Info, ref = SockRef}, Level, Key, Value) -> Reason :: term() ; (Socket, ip, ip_socket_option()) -> {ok, Value} | {error, Reason} when Socket :: socket(), - Level :: ip, - Key :: ip_socket_option(), Value :: term(), Reason :: term() ; (Socket, ipv6, ipv6_socket_option()) -> {ok, Value} | {error, Reason} when -- cgit v1.2.3 From cb858ed68f2cc21014f37c8f6c1cb0dfc20f6184 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 13 Jun 2018 10:54:01 +0200 Subject: [socket-nif] The info function now returns a non-empty map The map returned by the info function is now populated with the "global stuff". That is, debug, iow and the (global) counters (which are still not actually incremented). Also added debug functions and macros (not yet used). OTP-14831 --- erts/preloaded/src/socket.erl | 75 +++++++------------------------------------ 1 file changed, 12 insertions(+), 63 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 90fe5b9f36..bab4fce3f3 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -49,12 +49,7 @@ shutdown/2, setopt/4, - getopt/3, - - %% Some IPv6 utility functions - link_if2idx/1, - link_idx2if/1, - link_ifs/0 + getopt/3 ]). -export_type([ @@ -91,12 +86,11 @@ ip_tos_flag/0 ]). - %% We support only a subset of all domains. -type domain() :: local | inet | inet6. %% We support only a subset of all types. --type type() :: stream | dgram | raw | seqpacket. +-type type() :: stream | dgram | raw | rdm | seqpacket. %% We support only a subset of all protocols: -type protocol() :: ip | tcp | udp | sctp. @@ -539,6 +533,15 @@ info() -> %% %% nif_register(SockRef, self()). %% +%% +%% +%% Maybe register the process under a name? +%% Something like: +%% +%% list_to_atom(lists:flatten(io_lib:format("socket-~p", [SockRef]))). +%% +%% +%% %% The nif sets up a monitor to this process, and if it dies the socket %% is closed. It is also used if someone wants to monitor the socket. %% @@ -548,6 +551,7 @@ info() -> %% socket:demonitor(Socket) %% %% These are basically used to monitor the controller process. +%% Should the socket record therefor contain the pid of the controller process? %% %% %% @@ -1509,53 +1513,6 @@ getopt(#socket{info = Info, ref = SockRef}, Level, Key) -> -%% =========================================================================== -%% -%% link_if2idx - Mappings between network interface names and indexes: if -> idx -%% -%% - --spec link_if2idx(If) -> {ok, Idx} | {error, Reason} when - If :: string(), - Idx :: non_neg_integer(), - Reason :: term(). - -link_if2idx(If) when is_list(If) -> - nif_link_if2idx(If). - - -%% =========================================================================== -%% -%% link_idx2if - Mappings between network interface names and indexes: idx -> if -%% -%% - --spec link_idx2if(Idx) -> {ok, If} | {error, Reason} when - Idx :: non_neg_integer(), - If :: string(), - Reason :: term(). - -link_idx2if(Idx) when is_integer(Idx) -> - nif_link_idx2if(Idx). - - - -%% =========================================================================== -%% -%% link_ifs - get network interface names and indexes -%% -%% - --spec link_ifs() -> Names | {error, Reason} when - Names :: [{Idx, If}], - Idx :: non_neg_integer(), - If :: string(), - Reason :: term(). - -link_ifs() -> - nif_link_ifs(). - - %% =========================================================================== %% @@ -2233,11 +2190,3 @@ nif_setopt(_Ref, _IsEnc, _Lev, _Key, _Val) -> nif_getopt(_Ref, _IsEnc, _Lev, _Key) -> erlang:error(badarg). -nif_link_if2idx(_Name) -> - erlang:error(badarg). - -nif_link_idx2if(_Id) -> - erlang:error(badarg). - -nif_link_ifs() -> - erlang:error(badarg). -- cgit v1.2.3 From 1f4a0eb6629d813be8656db239ee5b98a78088a9 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 13 Jun 2018 12:42:56 +0200 Subject: [socket-nif] Added some use of debug and fixed bind Added (some) use of the debug printouts in (nif-) open and bind. Also fixed handling of the address argument in the bind function(s) (since it was changed to be of the in_sockaddr()). OTP-14831 --- erts/preloaded/src/socket.erl | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index bab4fce3f3..5a0748e8fb 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -324,7 +324,9 @@ mincost | integer(). --type socket_info() :: map(). +-type socket_info() :: #{domain => domain(), + type => type(), + protocol => protocol()}. -record(socket, {info :: socket_info(), ref :: reference()}). %% -opaque socket() :: {socket, socket_info(), reference()}. @@ -671,10 +673,17 @@ bind(Socket, File) when is_list(File) andalso (File =/= []) -> true -> {error, einval} end; -bind(#socket{ref = SockRef} = _Socket, SockAddr) - when is_record(SockAddr, in4_sockaddr) orelse - is_record(SockAddr, in6_sockaddr) orelse - (SockAddr =:= any) orelse (SockAddr =:= loopback) -> +bind(#socket{info = #{domain := inet}} = Socket, Addr) + when ((Addr =:= any) orelse (Addr =:= loopback)) -> + bind(Socket, #in4_sockaddr{addr = Addr}); +bind(#socket{info = #{domain := inet6}} = Socket, Addr) + when ((Addr =:= any) orelse (Addr =:= loopback)) -> + bind(Socket, #in6_sockaddr{addr = Addr}); +bind(#socket{info = #{domain := inet}, ref = SockRef} = _Socket, SockAddr) + when is_record(SockAddr, in4_sockaddr) -> + nif_bind(SockRef, SockAddr); +bind(#socket{info = #{domain := inet6}, ref = SockRef} = _Socket, SockAddr) + when is_record(SockAddr, in6_sockaddr) -> nif_bind(SockRef, SockAddr). -- cgit v1.2.3 From 2d6517fe41bebdd73dfc2d24a29b417587234ef1 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 29 May 2018 10:42:51 +0200 Subject: [net-nif] Erlang interface module for net This is part of the nififying of the inet driver. OTP-14831 --- erts/preloaded/src/Makefile | 1 + erts/preloaded/src/net.erl | 357 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 358 insertions(+) create mode 100644 erts/preloaded/src/net.erl (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile index aa9390b038..213dc2a1a2 100644 --- a/erts/preloaded/src/Makefile +++ b/erts/preloaded/src/Makefile @@ -36,6 +36,7 @@ include $(ERL_TOP)/lib/kernel/vsn.mk PRE_LOADED_ERL_MODULES = \ erl_prim_loader \ init \ + net \ prim_buffer \ prim_file \ prim_inet \ diff --git a/erts/preloaded/src/net.erl b/erts/preloaded/src/net.erl new file mode 100644 index 0000000000..39c907eca3 --- /dev/null +++ b/erts/preloaded/src/net.erl @@ -0,0 +1,357 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2018-2018. 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. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(net). + +%% Administrative and "global" utility functions +-export([ + on_load/0, on_load/1, on_load/2, + info/0 + ]). + +-export([ + getnameinfo/1, getnameinfo/2, + getaddrinfo/2, + + if_name2index/1, + if_index2name/1, + if_names/0 + ]). + +-export_type([ + ip_address/0, + ip4_address/0, + ip6_address/0, + in_sockaddr/0, + in4_sockaddr/0, + in6_sockaddr/0, + port_number/0, + + address_info/0, + name_info/0, + + name_info_flags/0, + name_info_flag/0, + name_info_flag_ext/0, + + network_interface_name/0, + network_interface_index/0 + ]). + +-record(name_info, {flags, host, service}). +-record(address_info, {flags, family, socket_type, protocol, addr}). + +%% Many of these should be moved to the socket module. +-type ip_address() :: ip4_address() | ip6_address(). +-type ip4_address() :: {0..255, 0..255, 0..255, 0..255}. +-type ip6_address() :: + {0..65535, + 0..65535, + 0..65535, + 0..65535, + 0..65535, + 0..65535, + 0..65535, + 0..65535}. +-type uint20() :: 0..16#FFFFF. +-type uint32() :: 0..16#FFFFFFFF. +-type in6_flow_info() :: uint20(). +-type in6_scope_id() :: uint32(). +-record(in4_sockaddr, {port = 0 :: port_number(), + addr = any :: any | loopback | ip4_address()}). +-type in4_sockaddr() :: #in4_sockaddr{}. +-record(in6_sockaddr, {port = 0 :: port_number(), + addr = any :: any | loopback | ip6_address(), + flowinfo = 0 :: in6_flow_info(), + scope_id = 0 :: in6_scope_id()}). +-type in6_sockaddr() :: #in6_sockaddr{}. + +-type in_sockaddr() :: in4_sockaddr() | in6_sockaddr(). + +-type port_number() :: 0..65535. + +-type name_info_flags() :: [name_info_flag()|name_info_flag_ext()]. +-type name_info_flag() :: namereqd | + dgram | + nofqdn | + numerichost | + nomericserv. +-type name_info_flag_ext() :: idn | + idna_allow_unassigned | + idna_use_std3_ascii_rules. +-type name_info() :: #name_info{}. +-type address_info() :: #address_info{}. +-type network_interface_name() :: string(). +-type network_interface_index() :: non_neg_integer(). + + +-define(NET_NAME_INFO_NAMEREQD, 0). +-define(NET_NAME_INFO_DGRAM, 1). +-define(NET_NAME_INFO_NOFQDN, 2). +-define(NET_NAME_INFO_NUMERICHOST, 3). +-define(NET_NAME_INFO_NUMERICSERV, 4). +-define(NET_NAME_INFO_IDN, 5). +-define(NET_NAME_INFO_IDNA_ALLOW_UNASSIGNED, 6). +-define(NET_NAME_INFO_IDNA_USE_STD3_ASCII_RULES, 7). + + +%% =========================================================================== +%% +%% Administrative and utility API +%% +%% =========================================================================== + +-spec on_load() -> ok. + +%% Should we require that the Extra arg is a map? +on_load() -> + on_load(#{}). + +-spec on_load(Extra) -> ok when + Extra :: maps:map(). + +on_load(Extra) when is_map(Extra) -> + on_load(atom_to_list(?MODULE), Extra). + +-spec on_load(Path, Extra) -> ok when + Path :: string(), + Extra :: maps:map(). + +on_load(Path, Extra) when is_list(Path) andalso is_map(Extra) -> + on_load(nif_is_loaded(), Path, Extra). + +on_load(true, _Path, _Extra) -> + ok; +on_load(false, Path, Extra) -> + ok = erlang:load_nif(Path, maps:put(timestamp, formated_timestamp(), Extra)). + + + +-spec info() -> list(). + +info() -> + nif_info(). + + + +%% =========================================================================== +%% +%% The proper net API +%% +%% =========================================================================== + +%% =========================================================================== +%% +%% getnameinfo - Address-to-name translation in protocol-independent manner. +%% +%% + +-spec getnameinfo(SockAddr) -> {ok, Info} | {error, Reason} when + SockAddr :: in_sockaddr(), + Info :: name_info(), + Reason :: term(). + +getnameinfo(SockAddr) + when is_record(SockAddr, in4_sockaddr) orelse + is_record(SockAddr, in6_sockaddr) -> + getnameinfo(SockAddr, []). + +-spec getnameinfo(SockAddr, Flags) -> {ok, Info} | {error, Reason} when + SockAddr :: in_sockaddr(), + Flags :: name_info_flags(), + Info :: name_info(), + Reason :: term(). + +getnameinfo(SockAddr, Flags) + when (is_record(SockAddr, in4_sockaddr) orelse + is_record(SockAddr, in6_sockaddr)) andalso + is_list(Flags) -> + try + begin + EFlags = enc_name_info_flags(Flags), + nif_getnameinfo(SockAddr, EFlags) + end + catch + throw:T -> + T; + error:Reason -> + {error, Reason} + end. + + +enc_name_info_flags([]) -> + 0; +enc_name_info_flags(Flags) -> + EFlags = [{namereqd, ?NET_NAME_INFO_NAMEREQD}, + {dgram, ?NET_NAME_INFO_DGRAM}, + {nofqdn, ?NET_NAME_INFO_NOFQDN}, + {numerichost, ?NET_NAME_INFO_NUMERICHOST}, + {numericserv, ?NET_NAME_INFO_NUMERICSERV}, + + %% The below flags was introduce with glibc 2.3.4. + {idn, ?NET_NAME_INFO_IDN}, + {idna_allow_unassigned, ?NET_NAME_INFO_IDNA_ALLOW_UNASSIGNED}, + {idna_use_std3_ascii_rules,?NET_NAME_INFO_IDNA_USE_STD3_ASCII_RULES}], + enc_flags(Flags, EFlags). + + +enc_flags([], _) -> + 0; +enc_flags(Flags, EFlags) -> + F = fun(Flag, Acc) -> + case lists:keysearch(Flag, 1, EFlags) of + {value, {Flag, EFlag}} -> + Acc bor (1 bsl EFlag); + false -> + throw({error, {unknown_flag, Flag}}) + end + end, + lists:foldl(F, 0, Flags). + + +%% =========================================================================== +%% +%% getaddrinfo - Network address and service translation +%% +%% There is also a "hint" argument that we "at some point" should implement. + +-spec getaddrinfo(Host, Service) -> {ok, Info} | {error, Reason} when + Host :: string(), + Service :: string(), + Info :: [address_info()], + Reason :: term(). + +getaddrinfo(Host, Service) + when (is_list(Host) orelse (Host =:= undefined)) andalso + (is_list(Service) orelse (Service =:= undefined)) andalso + (not ((Service =:= undefined) andalso (Host =:= undefined))) -> + nif_getaddrinfo(Host, Service, undefined). + + + +%% =========================================================================== +%% +%% if_name2index - Mappings between network interface names and indexes: +%% name -> idx +%% +%% + +-spec if_name2index(Name) -> {ok, Idx} | {error, Reason} when + Name :: string(), + Idx :: non_neg_integer(), + Reason :: term(). + +if_name2index(If) when is_list(If) -> + nif_if_name2index(If). + + + +%% =========================================================================== +%% +%% if_index2name - Mappings between network interface names and indexes: +%% idx -> name +%% +%% + +-spec if_index2name(Idx) -> {ok, Name} | {error, Reason} when + Idx :: non_neg_integer(), + Name :: string(), + Reason :: term(). + +if_index2name(Idx) when is_integer(Idx) -> + nif_if_index2name(Idx). + + + +%% =========================================================================== +%% +%% if_names - Get network interface names and indexes +%% +%% + +-spec if_names() -> Names | {error, Reason} when + Names :: [{Idx, If}], + Idx :: non_neg_integer(), + If :: string(), + Reason :: term(). + +if_names() -> + nif_if_names(). + + + +%% =========================================================================== +%% +%% Misc utility functions +%% +%% =========================================================================== + +formated_timestamp() -> + format_timestamp(os:timestamp()). + +format_timestamp(Now) -> + N2T = fun(N) -> calendar:now_to_local_time(N) end, + format_timestamp(Now, N2T, true). + +format_timestamp({_N1, _N2, N3} = N, N2T, true) -> + FormatExtra = ".~.2.0w", + ArgsExtra = [N3 div 10000], + format_timestamp(N, N2T, FormatExtra, ArgsExtra); +format_timestamp({_N1, _N2, _N3} = N, N2T, false) -> + FormatExtra = "", + ArgsExtra = [], + format_timestamp(N, N2T, FormatExtra, ArgsExtra). + +format_timestamp(N, N2T, FormatExtra, ArgsExtra) -> + {Date, Time} = N2T(N), + {YYYY,MM,DD} = Date, + {Hour,Min,Sec} = Time, + FormatDate = + io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w" ++ FormatExtra, + [YYYY, MM, DD, Hour, Min, Sec] ++ ArgsExtra), + lists:flatten(FormatDate). + + +%% =========================================================================== +%% +%% The actual NIF-functions. +%% +%% =========================================================================== + +nif_is_loaded() -> + false. + +nif_info() -> + erlang:error(badarg). + +nif_getnameinfo(_Addr, _Flags) -> + erlang:error(badarg). + +nif_getaddrinfo(_Host, _Service, _Hints) -> + erlang:error(badarg). + +nif_if_name2index(_Name) -> + erlang:error(badarg). + +nif_if_index2name(_Id) -> + erlang:error(badarg). + +nif_if_names() -> + erlang:error(badarg). -- cgit v1.2.3 From 6b2c750c53a288d999c0f63dc6fe26a22d63ed00 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 29 May 2018 10:54:55 +0200 Subject: [net-nif] Added preliminary net nif file OTP-14831 --- erts/preloaded/src/net.erl | 55 +++------------------------------------------- 1 file changed, 3 insertions(+), 52 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/net.erl b/erts/preloaded/src/net.erl index 39c907eca3..bdd81ea93a 100644 --- a/erts/preloaded/src/net.erl +++ b/erts/preloaded/src/net.erl @@ -55,8 +55,6 @@ network_interface_index/0 ]). --record(name_info, {flags, host, service}). --record(address_info, {flags, family, socket_type, protocol, addr}). %% Many of these should be moved to the socket module. -type ip_address() :: ip4_address() | ip6_address(). @@ -96,21 +94,14 @@ -type name_info_flag_ext() :: idn | idna_allow_unassigned | idna_use_std3_ascii_rules. +-record(name_info, {host, service}). -type name_info() :: #name_info{}. +-record(address_info, {family, socktype, protocol, addr}). -type address_info() :: #address_info{}. -type network_interface_name() :: string(). -type network_interface_index() :: non_neg_integer(). --define(NET_NAME_INFO_NAMEREQD, 0). --define(NET_NAME_INFO_DGRAM, 1). --define(NET_NAME_INFO_NOFQDN, 2). --define(NET_NAME_INFO_NUMERICHOST, 3). --define(NET_NAME_INFO_NUMERICSERV, 4). --define(NET_NAME_INFO_IDN, 5). --define(NET_NAME_INFO_IDNA_ALLOW_UNASSIGNED, 6). --define(NET_NAME_INFO_IDNA_USE_STD3_ASCII_RULES, 7). - %% =========================================================================== %% @@ -183,47 +174,7 @@ getnameinfo(SockAddr, Flags) when (is_record(SockAddr, in4_sockaddr) orelse is_record(SockAddr, in6_sockaddr)) andalso is_list(Flags) -> - try - begin - EFlags = enc_name_info_flags(Flags), - nif_getnameinfo(SockAddr, EFlags) - end - catch - throw:T -> - T; - error:Reason -> - {error, Reason} - end. - - -enc_name_info_flags([]) -> - 0; -enc_name_info_flags(Flags) -> - EFlags = [{namereqd, ?NET_NAME_INFO_NAMEREQD}, - {dgram, ?NET_NAME_INFO_DGRAM}, - {nofqdn, ?NET_NAME_INFO_NOFQDN}, - {numerichost, ?NET_NAME_INFO_NUMERICHOST}, - {numericserv, ?NET_NAME_INFO_NUMERICSERV}, - - %% The below flags was introduce with glibc 2.3.4. - {idn, ?NET_NAME_INFO_IDN}, - {idna_allow_unassigned, ?NET_NAME_INFO_IDNA_ALLOW_UNASSIGNED}, - {idna_use_std3_ascii_rules,?NET_NAME_INFO_IDNA_USE_STD3_ASCII_RULES}], - enc_flags(Flags, EFlags). - - -enc_flags([], _) -> - 0; -enc_flags(Flags, EFlags) -> - F = fun(Flag, Acc) -> - case lists:keysearch(Flag, 1, EFlags) of - {value, {Flag, EFlag}} -> - Acc bor (1 bsl EFlag); - false -> - throw({error, {unknown_flag, Flag}}) - end - end, - lists:foldl(F, 0, Flags). + nif_getnameinfo(SockAddr, EFlags). %% =========================================================================== -- cgit v1.2.3 From 5b7ac9423f07171e4cd682a216cc86b1d32c660a Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 8 Jun 2018 15:25:51 +0200 Subject: [net-nif] The net-module now actually loads The net (nif) module now actually loads (automatically) when the VM is started (*on linux*). Now we must make sure it *actually* works, and implement the rest of the stuff... --- erts/preloaded/src/init.erl | 1 + erts/preloaded/src/net.erl | 52 ++++++++++++++++++++++----------------------- 2 files changed, 27 insertions(+), 26 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index 303301cbff..b02e5dce85 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -206,6 +206,7 @@ boot(BootArgs) -> erl_tracer:on_load(), prim_buffer:on_load(), prim_file:on_load(), + net:on_load(), {Start0,Flags,Args} = parse_boot_args(BootArgs), %% We don't get to profile parsing of BootArgs diff --git a/erts/preloaded/src/net.erl b/erts/preloaded/src/net.erl index bdd81ea93a..f145797b97 100644 --- a/erts/preloaded/src/net.erl +++ b/erts/preloaded/src/net.erl @@ -131,7 +131,7 @@ on_load(Path, Extra) when is_list(Path) andalso is_map(Extra) -> on_load(true, _Path, _Extra) -> ok; on_load(false, Path, Extra) -> - ok = erlang:load_nif(Path, maps:put(timestamp, formated_timestamp(), Extra)). + ok = erlang:load_nif(Path, Extra). @@ -174,7 +174,7 @@ getnameinfo(SockAddr, Flags) when (is_record(SockAddr, in4_sockaddr) orelse is_record(SockAddr, in6_sockaddr)) andalso is_list(Flags) -> - nif_getnameinfo(SockAddr, EFlags). + nif_getnameinfo(SockAddr, Flags). %% =========================================================================== @@ -254,30 +254,30 @@ if_names() -> %% %% =========================================================================== -formated_timestamp() -> - format_timestamp(os:timestamp()). - -format_timestamp(Now) -> - N2T = fun(N) -> calendar:now_to_local_time(N) end, - format_timestamp(Now, N2T, true). - -format_timestamp({_N1, _N2, N3} = N, N2T, true) -> - FormatExtra = ".~.2.0w", - ArgsExtra = [N3 div 10000], - format_timestamp(N, N2T, FormatExtra, ArgsExtra); -format_timestamp({_N1, _N2, _N3} = N, N2T, false) -> - FormatExtra = "", - ArgsExtra = [], - format_timestamp(N, N2T, FormatExtra, ArgsExtra). - -format_timestamp(N, N2T, FormatExtra, ArgsExtra) -> - {Date, Time} = N2T(N), - {YYYY,MM,DD} = Date, - {Hour,Min,Sec} = Time, - FormatDate = - io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w" ++ FormatExtra, - [YYYY, MM, DD, Hour, Min, Sec] ++ ArgsExtra), - lists:flatten(FormatDate). +%% formated_timestamp() -> +%% format_timestamp(os:timestamp()). + +%% format_timestamp(Now) -> +%% N2T = fun(N) -> calendar:now_to_local_time(N) end, +%% format_timestamp(Now, N2T, true). + +%% format_timestamp({_N1, _N2, N3} = N, N2T, true) -> +%% FormatExtra = ".~.2.0w", +%% ArgsExtra = [N3 div 10000], +%% format_timestamp(N, N2T, FormatExtra, ArgsExtra); +%% format_timestamp({_N1, _N2, _N3} = N, N2T, false) -> +%% FormatExtra = "", +%% ArgsExtra = [], +%% format_timestamp(N, N2T, FormatExtra, ArgsExtra). + +%% format_timestamp(N, N2T, FormatExtra, ArgsExtra) -> +%% {Date, Time} = N2T(N), +%% {YYYY,MM,DD} = Date, +%% {Hour,Min,Sec} = Time, +%% FormatDate = +%% io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w" ++ FormatExtra, +%% [YYYY, MM, DD, Hour, Min, Sec] ++ ArgsExtra), +%% lists:flatten(FormatDate). %% =========================================================================== -- cgit v1.2.3 From cc08971508f3515f9f3b6c406ff0f6a186b24f6b Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 11 Jun 2018 12:34:23 +0200 Subject: [net-nif] Added debugging Added a command function, which is currently only used to enable and disable debug, which is also added. --- erts/preloaded/src/net.erl | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/net.erl b/erts/preloaded/src/net.erl index f145797b97..3c010871c0 100644 --- a/erts/preloaded/src/net.erl +++ b/erts/preloaded/src/net.erl @@ -23,7 +23,8 @@ %% Administrative and "global" utility functions -export([ on_load/0, on_load/1, on_load/2, - info/0 + info/0, + command/1 ]). -export([ @@ -141,6 +142,12 @@ info() -> nif_info(). +-spec command(Cmd :: term()) -> term(). + +command(Cmd) -> + nif_command(Cmd). + + %% =========================================================================== %% @@ -292,6 +299,9 @@ nif_is_loaded() -> nif_info() -> erlang:error(badarg). +nif_command(_Cmd) -> + erlang:error(badarg). + nif_getnameinfo(_Addr, _Flags) -> erlang:error(badarg). -- cgit v1.2.3 From f2a28200a826af65596bb554b014d2c93b6314a7 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 11 Jun 2018 14:40:58 +0200 Subject: [net-nif] Removed the old net module The old net module (in kernel) (deprecated) was removed and its function(s) has been moved into the new module. Also a minor updated to the info function. OTP-14831 --- erts/preloaded/src/net.erl | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/net.erl b/erts/preloaded/src/net.erl index 3c010871c0..54b3790dd5 100644 --- a/erts/preloaded/src/net.erl +++ b/erts/preloaded/src/net.erl @@ -36,6 +36,14 @@ if_names/0 ]). +%% Deprecated functions from the "old" net module +-export([call/4, + cast/4, + broadcast/3, + ping/1, + relay/1, + sleep/1]). + -export_type([ ip_address/0, ip4_address/0, @@ -103,6 +111,20 @@ -type network_interface_index() :: non_neg_integer(). +%% =========================================================================== +%% +%% D E P R E C A T E D F U N C T I O N S +%% +%% =========================================================================== + +call(N,M,F,A) -> rpc:call(N,M,F,A). +cast(N,M,F,A) -> rpc:cast(N,M,F,A). +broadcast(M,F,A) -> rpc:eval_everywhere(M,F,A). +ping(Node) -> net_adm:ping(Node). +sleep(T) -> receive after T -> ok end. +relay(X) -> slave:relay(X). + + %% =========================================================================== %% -- cgit v1.2.3 From 8c6d495f54207d019e645e5ff726418677f92ab9 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 11 Jun 2018 18:14:42 +0200 Subject: [net-nif] Implemented gethostname --- erts/preloaded/src/net.erl | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/net.erl b/erts/preloaded/src/net.erl index 54b3790dd5..823f11e2b8 100644 --- a/erts/preloaded/src/net.erl +++ b/erts/preloaded/src/net.erl @@ -28,6 +28,7 @@ ]). -export([ + gethostname/0, getnameinfo/1, getnameinfo/2, getaddrinfo/2, @@ -177,6 +178,20 @@ command(Cmd) -> %% %% =========================================================================== +%% =========================================================================== +%% +%% gethostname - Get the name of the current host. +%% +%% + +-spec gethostname() -> {ok, HostName} | {error, Reason} when + HostName :: string(), + Reason :: term(). + +gethostname() -> + nif_gethostname(). + + %% =========================================================================== %% %% getnameinfo - Address-to-name translation in protocol-independent manner. @@ -324,6 +339,9 @@ nif_info() -> nif_command(_Cmd) -> erlang:error(badarg). +nif_gethostname() -> + erlang:error(badarg). + nif_getnameinfo(_Addr, _Flags) -> erlang:error(badarg). -- cgit v1.2.3 From ef34944c970f842a7406f59c827243c8be77fdc2 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 12 Jun 2018 15:47:29 +0200 Subject: [net-nif] Fixed getnameinfo And now fixed the getnameinfo function. OTP-14831 --- erts/preloaded/src/net.erl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/net.erl b/erts/preloaded/src/net.erl index 823f11e2b8..29739f4510 100644 --- a/erts/preloaded/src/net.erl +++ b/erts/preloaded/src/net.erl @@ -206,18 +206,20 @@ gethostname() -> getnameinfo(SockAddr) when is_record(SockAddr, in4_sockaddr) orelse is_record(SockAddr, in6_sockaddr) -> - getnameinfo(SockAddr, []). + getnameinfo(SockAddr, undefined). -spec getnameinfo(SockAddr, Flags) -> {ok, Info} | {error, Reason} when SockAddr :: in_sockaddr(), - Flags :: name_info_flags(), + Flags :: name_info_flags() | undefined, Info :: name_info(), Reason :: term(). +getnameinfo(SockAddr, [] = _Flags) -> + getnameinfo(SockAddr, undefined); getnameinfo(SockAddr, Flags) when (is_record(SockAddr, in4_sockaddr) orelse is_record(SockAddr, in6_sockaddr)) andalso - is_list(Flags) -> + (is_list(Flags) orelse (Flags =:= undefined)) -> nif_getnameinfo(SockAddr, Flags). -- cgit v1.2.3 From 1b31432a2c60364dc3e7b2a18fa8494475344271 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 15 Jun 2018 18:51:48 +0200 Subject: [socket+net-nif] Introduced a couple of common files Started to move the common stuff, such as common utility functions, debug and encode / decode of basic types. OTP-14831 --- erts/preloaded/src/Makefile | 2 +- erts/preloaded/src/net.erl | 87 ++++++++---------- erts/preloaded/src/socket.erl | 209 ++++++++++++++++++++++++++++-------------- 3 files changed, 176 insertions(+), 122 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile index 213dc2a1a2..fae60a4491 100644 --- a/erts/preloaded/src/Makefile +++ b/erts/preloaded/src/Makefile @@ -36,11 +36,11 @@ include $(ERL_TOP)/lib/kernel/vsn.mk PRE_LOADED_ERL_MODULES = \ erl_prim_loader \ init \ - net \ prim_buffer \ prim_file \ prim_inet \ socket \ + net \ zlib \ prim_zip \ otp_ring0 \ diff --git a/erts/preloaded/src/net.erl b/erts/preloaded/src/net.erl index 29739f4510..6abfc22d3f 100644 --- a/erts/preloaded/src/net.erl +++ b/erts/preloaded/src/net.erl @@ -30,7 +30,7 @@ -export([ gethostname/0, getnameinfo/1, getnameinfo/2, - getaddrinfo/2, + getaddrinfo/1, getaddrinfo/2, if_name2index/1, if_index2name/1, @@ -46,14 +46,6 @@ sleep/1]). -export_type([ - ip_address/0, - ip4_address/0, - ip6_address/0, - in_sockaddr/0, - in4_sockaddr/0, - in6_sockaddr/0, - port_number/0, - address_info/0, name_info/0, @@ -66,35 +58,6 @@ ]). -%% Many of these should be moved to the socket module. --type ip_address() :: ip4_address() | ip6_address(). --type ip4_address() :: {0..255, 0..255, 0..255, 0..255}. --type ip6_address() :: - {0..65535, - 0..65535, - 0..65535, - 0..65535, - 0..65535, - 0..65535, - 0..65535, - 0..65535}. --type uint20() :: 0..16#FFFFF. --type uint32() :: 0..16#FFFFFFFF. --type in6_flow_info() :: uint20(). --type in6_scope_id() :: uint32(). --record(in4_sockaddr, {port = 0 :: port_number(), - addr = any :: any | loopback | ip4_address()}). --type in4_sockaddr() :: #in4_sockaddr{}. --record(in6_sockaddr, {port = 0 :: port_number(), - addr = any :: any | loopback | ip6_address(), - flowinfo = 0 :: in6_flow_info(), - scope_id = 0 :: in6_scope_id()}). --type in6_sockaddr() :: #in6_sockaddr{}. - --type in_sockaddr() :: in4_sockaddr() | in6_sockaddr(). - --type port_number() :: 0..65535. - -type name_info_flags() :: [name_info_flag()|name_info_flag_ext()]. -type name_info_flag() :: namereqd | dgram | @@ -104,10 +67,19 @@ -type name_info_flag_ext() :: idn | idna_allow_unassigned | idna_use_std3_ascii_rules. --record(name_info, {host, service}). --type name_info() :: #name_info{}. --record(address_info, {family, socktype, protocol, addr}). --type address_info() :: #address_info{}. +-type name_info() :: #{host := string(), + service := string()}. +%% -record(name_info, {host, service}). +%% -type name_info() :: #name_info{}. +-type address_info() :: #{family := socket:domain(), + socktype := socket:type(), + protocol := socket:protocol(), + address := socket:sockaddr()}. +%% -record(address_info, {family :: socket:domain(), +%% socktype :: socket:type(), +%% protocol :: socket:protocol(), +%% addr :: undefined | socket:in_sockaddr() | string()}). +%% -type address_info() :: #address_info{}. -type network_interface_name() :: string(). -type network_interface_index() :: non_neg_integer(). @@ -199,38 +171,51 @@ gethostname() -> %% -spec getnameinfo(SockAddr) -> {ok, Info} | {error, Reason} when - SockAddr :: in_sockaddr(), + SockAddr :: socket:sockaddr(), Info :: name_info(), Reason :: term(). -getnameinfo(SockAddr) - when is_record(SockAddr, in4_sockaddr) orelse - is_record(SockAddr, in6_sockaddr) -> +getnameinfo(SockAddr) -> getnameinfo(SockAddr, undefined). -spec getnameinfo(SockAddr, Flags) -> {ok, Info} | {error, Reason} when - SockAddr :: in_sockaddr(), + SockAddr :: socket:sockaddr(), Flags :: name_info_flags() | undefined, Info :: name_info(), Reason :: term(). getnameinfo(SockAddr, [] = _Flags) -> getnameinfo(SockAddr, undefined); -getnameinfo(SockAddr, Flags) - when (is_record(SockAddr, in4_sockaddr) orelse - is_record(SockAddr, in6_sockaddr)) andalso +getnameinfo(#{family := Fam, addr := _Addr} = SockAddr, Flags) + when ((Fam =:= inet) orelse (Fam =:= inet6)) andalso (is_list(Flags) orelse (Flags =:= undefined)) -> + nif_getnameinfo(socket:ensure_sockaddr(SockAddr), Flags); +getnameinfo(#{family := Fam, path := _Path} = SockAddr, Flags) + when (Fam =:= local) andalso (is_list(Flags) orelse (Flags =:= undefined)) -> nif_getnameinfo(SockAddr, Flags). + %% =========================================================================== %% %% getaddrinfo - Network address and service translation %% %% There is also a "hint" argument that we "at some point" should implement. --spec getaddrinfo(Host, Service) -> {ok, Info} | {error, Reason} when +-spec getaddrinfo(Host) -> {ok, Info} | {error, Reason} when + Host :: string(), + Info :: [address_info()], + Reason :: term(). + +getaddrinfo(Host) when is_list(Host) -> + getaddrinfo(Host, undefined). + + +-spec getaddrinfo(Host, undefined) -> {ok, Info} | {error, Reason} when Host :: string(), + Info :: [address_info()], + Reason :: term() + ; (undefined, Service) -> {ok, Info} | {error, Reason} when Service :: string(), Info :: [address_info()], Reason :: term(). diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 5a0748e8fb..b89fa06da8 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -25,7 +25,8 @@ %% Administrative and "global" utility functions -export([ on_load/0, on_load/1, on_load/2, - info/0 + info/0, + ensure_sockaddr/1 ]). -export([ @@ -58,13 +59,14 @@ protocol/0, socket/0, + port_number/0, ip_address/0, ip4_address/0, ip6_address/0, - in_sockaddr/0, - in4_sockaddr/0, - in6_sockaddr/0, - port_number/0, + sockaddr/0, + sockaddr_in4/0, + sockaddr_in6/0, + sockaddr_un/0, accept_flags/0, accept_flag/0, @@ -95,6 +97,8 @@ %% We support only a subset of all protocols: -type protocol() :: ip | tcp | udp | sctp. +-type port_number() :: 0..65535. + -type ip_address() :: ip4_address() | ip6_address(). -type ip4_address() :: {0..255, 0..255, 0..255, 0..255}. @@ -115,22 +119,39 @@ 0..65535}. %% +%% %% Should we do these as maps instead? %% If we do we may need to include the family (domain) in the %% map (as the native type do. See struct sockaddr_in6). +%% +%% What about default values? Such as for port (=0) and addr (=any)? +%% %% --type in_sockaddr() :: in4_sockaddr() | in6_sockaddr(). --record(in4_sockaddr, {port = 0 :: port_number(), - addr = any :: any | loopback | ip4_address()}). --type in4_sockaddr() :: #in4_sockaddr{}. --record(in6_sockaddr, {port = 0 :: port_number(), - addr = any :: any | loopback | ip6_address(), - flowinfo = 0 :: in6_flow_info(), - scope_id = 0 :: in6_scope_id()}). --type in6_sockaddr() :: #in6_sockaddr{}. - --type port_number() :: 0..65535. +-type sockaddr_un() :: #{family := local, + path := binary() | string()}. +-type sockaddr_in4() :: #{family := inet, + port := port_number(), + addr := any | loopback | ip4_address()}. +-type sockaddr_in6() :: #{family := inet6, + port := port_number(), + addr := any | loopback | ip6_address(), + flowinfo := in6_flow_info(), + scope_id := in6_scope_id()}. +-type sockaddr() :: sockaddr_in4() | + sockaddr_in6() | + sockaddr_un(). + +-define(SOCKADDR_IN4_DEFAULTS(A), #{port => 0, + addr => A}). +-define(SOCKADDR_IN4_DEFAULTS, ?SOCKADDR_IN4_DEFAULTS(any)). +-define(SOCKADDR_IN4_DEFAULT(A), (?SOCKADDR_IN4_DEFAULTS(A))#{family => inet}). +-define(SOCKADDR_IN6_DEFAULTS(A), #{port => 0, + addr => A, + flowinfo => 0, + scope_id => 0}). +-define(SOCKADDR_IN6_DEFAULTS, ?SOCKADDR_IN6_DEFAULTS(any)). +-define(SOCKADDR_IN6_DEFAULT(A), (?SOCKADDR_IN6_DEFAULTS(A))#{family => inet6}). %% otp - The option is internal to our (OTP) imeplementation. %% socket - The socket layer (SOL_SOCKET). @@ -651,40 +672,61 @@ default_protocol(Protocol, _) -> Protocol. %% bind - bind a name to a socket %% --spec bind(Socket, FileOrAddr) -> ok | {error, Reason} when - Socket :: socket(), - FileOrAddr :: binary() | string() | in_sockaddr() | any | loopback, - Reason :: term(). +-spec bind(Socket, Addr) -> ok | {error, Reason} when + Socket :: socket(), + Addr :: any | loopback | sockaddr(), + Reason :: term(). -bind(Socket, File) when is_binary(File) -> - if - byte_size(File) =:= 0 -> - {error, einval}; - byte_size(File) =< 255 -> - nif_bind(Socket, File); - true -> - {error, einval} - end; -bind(Socket, File) when is_list(File) andalso (File =/= []) -> - Bin = unicode:characters_to_binary(File, file:native_name_encoding()), - if - byte_size(Bin) =< 255 -> - nif_bind(Socket, Bin); - true -> - {error, einval} - end; bind(#socket{info = #{domain := inet}} = Socket, Addr) when ((Addr =:= any) orelse (Addr =:= loopback)) -> - bind(Socket, #in4_sockaddr{addr = Addr}); + bind(Socket, ?SOCKADDR_IN4_DEFAULT(Addr)); bind(#socket{info = #{domain := inet6}} = Socket, Addr) when ((Addr =:= any) orelse (Addr =:= loopback)) -> - bind(Socket, #in6_sockaddr{addr = Addr}); -bind(#socket{info = #{domain := inet}, ref = SockRef} = _Socket, SockAddr) - when is_record(SockAddr, in4_sockaddr) -> - nif_bind(SockRef, SockAddr); -bind(#socket{info = #{domain := inet6}, ref = SockRef} = _Socket, SockAddr) - when is_record(SockAddr, in6_sockaddr) -> - nif_bind(SockRef, SockAddr). + bind(Socket, ?SOCKADDR_IN6_DEFAULT(Addr)); +bind(Socket, Addr) -> + try + begin + nif_bind(Socket, ensure_sockaddr(Addr)) + end + catch + throw:ERROR -> + ERROR + end. + +%% -spec bind(Socket, FileOrAddr) -> ok | {error, Reason} when +%% Socket :: socket(), +%% FileOrAddr :: binary() | string() | in_sockaddr() | any | loopback, +%% Reason :: term(). + +%% bind(Socket, File) when is_binary(File) -> +%% if +%% byte_size(File) =:= 0 -> +%% {error, einval}; +%% byte_size(File) =< 255 -> +%% nif_bind(Socket, File); +%% true -> +%% {error, einval} +%% end; +%% bind(Socket, File) when is_list(File) andalso (File =/= []) -> +%% Bin = unicode:characters_to_binary(File, file:native_name_encoding()), +%% if +%% byte_size(Bin) =< 255 -> +%% nif_bind(Socket, Bin); +%% true -> +%% {error, einval} +%% end; +%% bind(#socket{info = #{domain := inet}} = Socket, Addr) +%% when ((Addr =:= any) orelse (Addr =:= loopback)) -> +%% bind(Socket, #in4_sockaddr{addr = Addr}); +%% bind(#socket{info = #{domain := inet6}} = Socket, Addr) +%% when ((Addr =:= any) orelse (Addr =:= loopback)) -> +%% bind(Socket, #in6_sockaddr{addr = Addr}); +%% bind(#socket{info = #{domain := inet}, ref = SockRef} = _Socket, SockAddr) +%% when is_record(SockAddr, in4_sockaddr) -> +%% nif_bind(SockRef, SockAddr); +%% bind(#socket{info = #{domain := inet6}, ref = SockRef} = _Socket, SockAddr) +%% when is_record(SockAddr, in6_sockaddr) -> +%% nif_bind(SockRef, SockAddr). @@ -695,7 +737,7 @@ bind(#socket{info = #{domain := inet6}, ref = SockRef} = _Socket, SockAddr) -spec connect(Socket, SockAddr) -> ok | {error, Reason} when Socket :: socket(), - SockAddr :: in_sockaddr(), + SockAddr :: sockaddr(), Reason :: term(). connect(Socket, SockAddr) -> @@ -703,16 +745,15 @@ connect(Socket, SockAddr) -> -spec connect(Socket, SockAddr, Timeout) -> ok | {error, Reason} when Socket :: socket(), - SockAddr :: in_sockaddr(), + SockAddr :: sockaddr(), Timeout :: timeout(), Reason :: term(). connect(_Socket, _SockAddr, Timeout) when (is_integer(Timeout) andalso (Timeout =< 0)) -> {error, timeout}; -connect(#socket{ref = SockRef}, SockAddr, Timeout) - when (is_record(SockAddr, in4_sockaddr) orelse - is_record(SockAddr, in6_sockaddr)) andalso +connect(#socket{ref = SockRef}, #{family := Fam} = SockAddr, Timeout) + when ((Fam =:= inet) orelse (Fam =:= inet6) orelse (Fam =:= local)) andalso ((Timeout =:= infinity) orelse is_integer(Timeout)) -> TS = timestamp(Timeout), case nif_connect(SockRef, SockAddr) of @@ -917,35 +958,40 @@ do_send(SockRef, Data, EFlags, Timeout) -> %% --------------------------------------------------------------------------- %% -sendto(Socket, Data, Flags, DestSockAddr) -> - sendto(Socket, Data, Flags, DestSockAddr, ?SOCKET_SENDTO_TIMEOUT_DEFAULT). +sendto(Socket, Data, Flags, Dest) -> + sendto(Socket, Data, Flags, Dest, ?SOCKET_SENDTO_TIMEOUT_DEFAULT). --spec sendto(Socket, Data, Flags, DestSockAddr, Timeout) -> +-spec sendto(Socket, Data, Flags, Dest, Timeout) -> ok | {error, Reason} when - Socket :: socket(), - Data :: binary(), - Flags :: send_flags(), - DestSockAddr :: null | in_sockaddr(), - Timeout :: timeout(), - Reason :: term(). + Socket :: socket(), + Data :: binary(), + Flags :: send_flags(), + Dest :: null | sockaddr(), + Timeout :: timeout(), + Reason :: term(). sendto(Socket, Data, Flags, DestSockAddr, Timeout) when is_list(Data) -> Bin = erlang:list_to_binary(Data), sendto(Socket, Bin, Flags, DestSockAddr, Timeout); -sendto(#socket{ref = SockRef}, Data, Flags, DestSockAddr, Timeout) +sendto(#socket{ref = SockRef}, Data, Flags, Dest, Timeout) + when is_binary(Data) andalso + is_list(Flags) andalso + (Dest =:= null) andalso + (is_integer(Timeout) orelse (Timeout =:= infinity)) -> + EFlags = enc_send_flags(Flags), + do_sendto(SockRef, Data, EFlags, Dest, Timeout); +sendto(#socket{ref = SockRef}, Data, Flags, #{family := Fam} = Dest, Timeout) when is_binary(Data) andalso is_list(Flags) andalso - (is_record(DestSockAddr, in4_sockaddr) orelse - is_record(DestSockAddr, in6_sockaddr) orelse - (DestSockAddr =:= null)) andalso + ((Fam =:= inet) orelse (Fam =:= inet6) orelse (Fam =:= local)) andalso (is_integer(Timeout) orelse (Timeout =:= infinity)) -> EFlags = enc_send_flags(Flags), - do_sendto(SockRef, Data, EFlags, DestSockAddr, Timeout). + do_sendto(SockRef, Data, EFlags, Dest, Timeout). -do_sendto(SockRef, Data, EFlags, DestSockAddr, Timeout) -> +do_sendto(SockRef, Data, EFlags, Dest, Timeout) -> TS = timestamp(Timeout), SendRef = make_ref(), - case nif_sendto(SockRef, SendRef, Data, EFlags, DestSockAddr) of + case nif_sendto(SockRef, SendRef, Data, EFlags, Dest) of ok -> %% We are done ok; @@ -955,10 +1001,10 @@ do_sendto(SockRef, Data, EFlags, DestSockAddr, Timeout) -> receive {select, SockRef, SendRef, ready_output} when (Written > 0) -> <<_:Written/binary, Rest/binary>> = Data, - do_sendto(SockRef, Rest, EFlags, DestSockAddr, + do_sendto(SockRef, Rest, EFlags, Dest, next_timeout(TS, Timeout)); {select, SockRef, SendRef, ready_output} -> - do_sendto(SockRef, Data, EFlags, DestSockAddr, + do_sendto(SockRef, Data, EFlags, Dest, next_timeout(TS, Timeout)); {nif_abort, SendRef, Reason} -> @@ -973,7 +1019,7 @@ do_sendto(SockRef, Data, EFlags, DestSockAddr, Timeout) -> {error, eagain} -> receive {select, SockRef, SendRef, ready_output} -> - do_sendto(SockRef, Data, EFlags, DestSockAddr, + do_sendto(SockRef, Data, EFlags, Dest, next_timeout(TS, Timeout)) after Timeout -> nif_cancel(SockRef, sendto, SendRef), @@ -1256,7 +1302,7 @@ recvfrom(Socket, BufSz, Timeout) -> BufSz :: non_neg_integer(), Flags :: recv_flags(), Timeout :: timeout(), - Source :: in_sockaddr() | string() | undefined, + Source :: sockaddr() | undefined, Data :: binary(), Reason :: term(). @@ -2061,6 +2107,26 @@ enc_shutdown_how(read_write) -> %% %% =========================================================================== +ensure_sockaddr(#{family := inet} = SockAddr) -> + maps:merge(?SOCKADDR_IN4_DEFAULTS, SockAddr); +ensure_sockaddr(#{family := inet6} = SockAddr) -> + maps:merge(?SOCKADDR_IN6_DEFAULTS, SockAddr); +ensure_sockaddr(#{family := local, path := Path} = SockAddr) + when is_list(Path) andalso + (length(Path) > 0) andalso + (length(Path) =< 255) -> + BinPath = unicode:characters_to_binary(Path, file:native_name_encoding()), + ensure_sockaddr(SockAddr#{path => BinPath}); +ensure_sockaddr(#{family := local, path := Path} = SockAddr) + when is_binary(Path) andalso + (byte_size(Path) > 0) andalso + (byte_size(Path) =< 255) -> + SockAddr; +ensure_sockaddr(_SockAddr) -> + einval(). + + + flush_select_msgs(LSRef, Ref) -> receive {select, LSRef, Ref, _} -> @@ -2136,6 +2202,9 @@ not_supported(What) -> unknown(What) -> error({unknown, What}). +einval() -> + error(einval). + error(Reason) -> throw({error, Reason}). -- cgit v1.2.3 From b63a16d0958bd748644d22f13f35f8956a903d6c Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 18 Jun 2018 18:19:39 +0200 Subject: [socket+net-nif] Moved common functions into util files The common stuff, like decode and encode of common types (soch as socket address), has been moved into a util file (socket_util). The debug stuff has also been moved into its own file. Also introduced a common include file for common macros and types. OTP-14831 --- erts/preloaded/src/socket.erl | 56 ++++++++++--------------------------------- 1 file changed, 12 insertions(+), 44 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index b89fa06da8..ba3ff6bab9 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -657,7 +657,7 @@ open(Domain, Type, Protocol0, Extra) when is_map(Extra) -> end. %% Note that this is just a convenience function for when the protocol was -%% not specified. If its actually specied, then that will be selected. +%% not specified. If its actually specified, then that will be selected. %% Also, this only works for the some of the type's (stream, dgram and %% seqpacket). default_protocol(null, stream) -> tcp; @@ -677,57 +677,22 @@ default_protocol(Protocol, _) -> Protocol. Addr :: any | loopback | sockaddr(), Reason :: term(). -bind(#socket{info = #{domain := inet}} = Socket, Addr) +bind(#socket{ref = SockRef, info = #{domain := inet}} = _Socket, Addr) when ((Addr =:= any) orelse (Addr =:= loopback)) -> - bind(Socket, ?SOCKADDR_IN4_DEFAULT(Addr)); -bind(#socket{info = #{domain := inet6}} = Socket, Addr) + nif_bind(SockRef, ?SOCKADDR_IN4_DEFAULT(Addr)); +bind(#socket{ref = SockRef, info = #{domain := inet6}} = _Socket, Addr) when ((Addr =:= any) orelse (Addr =:= loopback)) -> - bind(Socket, ?SOCKADDR_IN6_DEFAULT(Addr)); -bind(Socket, Addr) -> + nif_bind(SockRef, ?SOCKADDR_IN6_DEFAULT(Addr)); +bind(#socket{ref = SockRef} = _Socket, Addr) when is_map(Addr) -> try begin - nif_bind(Socket, ensure_sockaddr(Addr)) + nif_bind(SockRef, ensure_sockaddr(Addr)) end catch throw:ERROR -> ERROR end. -%% -spec bind(Socket, FileOrAddr) -> ok | {error, Reason} when -%% Socket :: socket(), -%% FileOrAddr :: binary() | string() | in_sockaddr() | any | loopback, -%% Reason :: term(). - -%% bind(Socket, File) when is_binary(File) -> -%% if -%% byte_size(File) =:= 0 -> -%% {error, einval}; -%% byte_size(File) =< 255 -> -%% nif_bind(Socket, File); -%% true -> -%% {error, einval} -%% end; -%% bind(Socket, File) when is_list(File) andalso (File =/= []) -> -%% Bin = unicode:characters_to_binary(File, file:native_name_encoding()), -%% if -%% byte_size(Bin) =< 255 -> -%% nif_bind(Socket, Bin); -%% true -> -%% {error, einval} -%% end; -%% bind(#socket{info = #{domain := inet}} = Socket, Addr) -%% when ((Addr =:= any) orelse (Addr =:= loopback)) -> -%% bind(Socket, #in4_sockaddr{addr = Addr}); -%% bind(#socket{info = #{domain := inet6}} = Socket, Addr) -%% when ((Addr =:= any) orelse (Addr =:= loopback)) -> -%% bind(Socket, #in6_sockaddr{addr = Addr}); -%% bind(#socket{info = #{domain := inet}, ref = SockRef} = _Socket, SockAddr) -%% when is_record(SockAddr, in4_sockaddr) -> -%% nif_bind(SockRef, SockAddr); -%% bind(#socket{info = #{domain := inet6}, ref = SockRef} = _Socket, SockAddr) -%% when is_record(SockAddr, in6_sockaddr) -> -%% nif_bind(SockRef, SockAddr). - %% =========================================================================== @@ -749,6 +714,9 @@ connect(Socket, SockAddr) -> Timeout :: timeout(), Reason :: term(). +%% +%% Is it possible to connect with family = local for the (dest) sockaddr? +%% connect(_Socket, _SockAddr, Timeout) when (is_integer(Timeout) andalso (Timeout =< 0)) -> {error, timeout}; @@ -970,9 +938,9 @@ sendto(Socket, Data, Flags, Dest) -> Timeout :: timeout(), Reason :: term(). -sendto(Socket, Data, Flags, DestSockAddr, Timeout) when is_list(Data) -> +sendto(Socket, Data, Flags, Dest, Timeout) when is_list(Data) -> Bin = erlang:list_to_binary(Data), - sendto(Socket, Bin, Flags, DestSockAddr, Timeout); + sendto(Socket, Bin, Flags, Dest, Timeout); sendto(#socket{ref = SockRef}, Data, Flags, Dest, Timeout) when is_binary(Data) andalso is_list(Flags) andalso -- cgit v1.2.3 From 24be0729fe3a1ccfd5f0713b565463d6557d8aa7 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 29 Jun 2018 18:23:55 +0200 Subject: [socket-nif] Fixed (stream) recv Fixed handling of closed in the recv function. We still need to properly handle when we get 0 bytes of data for other types ock sockets then stream (its valid for dgram for instance). OTP-14831 --- erts/preloaded/src/socket.erl | 288 +++++++++++++++++++++++++----------------- 1 file changed, 170 insertions(+), 118 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index ba3ff6bab9..bf94271073 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -41,7 +41,7 @@ %% sendmsg/4, %% writev/4, OR SENDV? It will be strange for recv then: recvv (instead of readv) - recv/2, recv/3, recv/4, + recv/1, recv/2, recv/3, recv/4, recvfrom/1, recvfrom/2, recvfrom/3, recvfrom/4, %% recvmsg/4, %% readv/3, @@ -442,16 +442,17 @@ -define(SOCKET_RECV_FLAGS_DEFAULT, []). -define(SOCKET_RECV_TIMEOUT_DEFAULT, infinity). --define(SOCKET_OPT_LEVEL_OTP, 0). --define(SOCKET_OPT_LEVEL_SOCKET, 1). --define(SOCKET_OPT_LEVEL_IP, 2). --define(SOCKET_OPT_LEVEL_IPV6, 3). --define(SOCKET_OPT_LEVEL_TCP, 4). --define(SOCKET_OPT_LEVEL_UDP, 5). --define(SOCKET_OPT_LEVEL_SCTP, 6). +-define(SOCKET_OPT_LEVEL_OTP, 0). +-define(SOCKET_OPT_LEVEL_SOCKET, 1). +-define(SOCKET_OPT_LEVEL_IP, 2). +-define(SOCKET_OPT_LEVEL_IPV6, 3). +-define(SOCKET_OPT_LEVEL_TCP, 4). +-define(SOCKET_OPT_LEVEL_UDP, 5). +-define(SOCKET_OPT_LEVEL_SCTP, 6). --define(SOCKET_OPT_OTP_DEBUG, 0). --define(SOCKET_OPT_OTP_IOW, 1). +-define(SOCKET_OPT_OTP_DEBUG, 0). +-define(SOCKET_OPT_OTP_IOW, 1). +-define(SOCKET_OPT_OTP_CTRL_PROC, 2). -define(SOCKET_OPT_SOCK_BROADCAST, 4). -define(SOCKET_OPT_SOCK_DONTROUTE, 7). @@ -1097,6 +1098,9 @@ do_sendto(SockRef, Data, EFlags, Dest, Timeout) -> %% Flags - A list of "options" for the read. %% Timeout - Time-out in milliseconds. +recv(Socket) -> + recv(Socket, 0). + recv(Socket, Length) -> recv(Socket, Length, ?SOCKET_RECV_FLAGS_DEFAULT, @@ -1131,10 +1135,21 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) (is_integer(Timeout) andalso (Timeout > 0)) -> TS = timestamp(Timeout), RecvRef = make_ref(), + p("do_recv -> try read with" + "~n SockRef: ~p" + "~n RecvRef: ~p" + "~n Length: ~p" + "~n EFlags: ~p" + "~nwhen" + "~n Timeout: ~p (~p)", [SockRef, RecvRef, Length, EFlags, Timeout, TS]), case nif_recv(SockRef, RecvRef, Length, EFlags) of {ok, true = _Complete, Bin} when (size(Acc) =:= 0) -> + p("do_recv -> ok: complete (size(Acc) =:= 0)" + "~n size(Bin): ~p", [size(Bin)]), {ok, Bin}; {ok, true = _Complete, Bin} -> + p("do_recv -> ok: complete" + "~n size(Bin): ~p", [size(Bin)]), {ok, <>}; %% It depends on the amount of bytes we tried to read: @@ -1143,12 +1158,16 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) %% > 0 - We got a part of the message and we will be notified %% when there is more to read (a select message) {ok, false = _Complete, Bin} when (Length =:= 0) -> + p("do_recv -> ok: not-complete (Length =:= 0)" + "~n size(Bin): ~p", [size(Bin)]), do_recv(SockRef, RecvRef, Length, EFlags, <>, next_timeout(TS, Timeout)); {ok, false = _Completed, Bin} when (size(Acc) =:= 0) -> + p("do_recv -> ok: not-complete (size(Acc) =:= 0)" + "~n size(Bin): ~p", [size(Bin)]), %% We got the first chunk of it. %% We will be notified (select message) when there %% is more to read. @@ -1171,6 +1190,8 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) end; {ok, false = _Completed, Bin} -> + p("do_recv -> ok: not-complete" + "~n size(Bin): ~p", [size(Bin)]), %% We got a chunk of it! NewTimeout = next_timeout(TS, Timeout), receive @@ -1190,16 +1211,19 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) {error, {timeout, Acc}} end; - %% We return with the accumulated binary regardless if its empty... - {error, eagain} when (Length =:= 0) -> + %% We return with the accumulated binary (if its non-empty) + {error, eagain} when (Length =:= 0) andalso (size(Acc) > 0) -> + p("do_recv -> eagain (Length =:= 0)", []), {ok, Acc}; {error, eagain} -> + p("do_recv -> eagain", []), %% There is nothing just now, but we will be notified when there %% is something to read (a select message). NewTimeout = next_timeout(TS, Timeout), receive {select, SockRef, RecvRef, ready_input} -> + p("do_recv -> received select ready-input message", []), do_recv(SockRef, RecvRef, Length, EFlags, Acc, @@ -1214,6 +1238,15 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) {error, timeout} end; + {error, closed = Reason} -> + do_close(SockRef), + if + (size(Acc) =:= 0) -> + {error, Reason}; + true -> + {error, {Reason, Acc}} + end; + {error, _} = ERROR when (size(Acc) =:= 0) -> ERROR; @@ -1342,6 +1375,9 @@ do_recvfrom(SockRef, BufSz, EFlags, Timeout) -> Reason :: term(). close(#socket{ref = SockRef}) -> + do_close(SockRef). + +do_close(SockRef) -> case nif_close(SockRef) of ok -> nif_finalize_close(SockRef); @@ -1659,6 +1695,8 @@ enc_setopt_value(otp, debug, V, _, _, _) when is_boolean(V) -> V; enc_setopt_value(otp, iow, V, _, _, _) when is_boolean(V) -> V; +enc_setopt_value(otp, controlling_process, V, _, _, _) when is_pid(V) -> + V; enc_setopt_value(otp = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); @@ -1859,27 +1897,31 @@ enc_sockopt_key(otp, debug, _, _, _, _) -> ?SOCKET_OPT_OTP_DEBUG; enc_sockopt_key(otp, iow, _, _, _, _) -> ?SOCKET_OPT_OTP_IOW; +enc_sockopt_key(otp, controlling_process, _, _, _, _) -> + ?SOCKET_OPT_OTP_CTRL_PROC; +enc_sockopt_key(otp = L, Opt, _, _, _, _) -> + not_supported({L, Opt}); %% +++ SOCKET socket options +++ -enc_sockopt_key(socket, acceptconn = Opt, get = _Dir, _D, _T, _P) -> - not_supported(Opt); +enc_sockopt_key(socket = L, acceptconn = Opt, get = _Dir, _D, _T, _P) -> + not_supported({L, Opt}); enc_sockopt_key(socket, acceptfilter = Opt, _Dir, _D, _T, _P) -> not_supported(Opt); %% Before linux 3.8, this socket option could be set. %% Size of buffer for name: IFNAMSZ %% So, we let the implementation decide. -enc_sockopt_key(socket, bindtodevide = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); +enc_sockopt_key(socket = L, bindtodevide = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); enc_sockopt_key(socket, broadcast = _Opt, _Dir, _D, dgram = _T, _P) -> ?SOCKET_OPT_SOCK_BROADCAST; -enc_sockopt_key(socket, busy_poll = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); -enc_sockopt_key(socket, debug = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); +enc_sockopt_key(socket = L, busy_poll = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(socket = L, debug = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); enc_sockopt_key(socket, dontroute = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SOCK_DONTROUTE; -enc_sockopt_key(socket, error = Opt, get = _Dir, _D, _T, _P) -> - not_supported(Opt); +enc_sockopt_key(socket = L, error = Opt, get = _Dir, _D, _T, _P) -> + not_supported({L, Opt}); %% This is only for connection-oriented sockets, but who are those? %% Type = stream or Protocol = tcp? %% For now, we just let is pass and it will fail later if not ok... @@ -1887,111 +1929,111 @@ enc_sockopt_key(socket, keepalive = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SOCK_KEEPALIVE; enc_sockopt_key(socket, linger = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SOCK_LINGER; -enc_sockopt_key(socket, mark = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); +enc_sockopt_key(socket = L, mark = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); enc_sockopt_key(socket, oobinline = Opt, _Dir, _D, _T, _P) -> not_supported(Opt); -enc_sockopt_key(socket, passcred = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); -enc_sockopt_key(socket, peek_off = Opt, _Dir, local = _D, _T, _P) -> - not_supported(Opt); -enc_sockopt_key(socket, peek_cred = Opt, get = _Dir, _D, _T, _P) -> - not_supported(Opt); +enc_sockopt_key(socket = L, passcred = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(socket = L, peek_off = Opt, _Dir, local = _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(socket = L, peek_cred = Opt, get = _Dir, _D, _T, _P) -> + not_supported({L, Opt}); enc_sockopt_key(socket, priority = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SOCK_PRIORITY; enc_sockopt_key(socket, rcvbuf = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SOCK_RCVBUF; -enc_sockopt_key(socket, rcvbufforce = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); +enc_sockopt_key(socket = L, rcvbufforce = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); %% May not work on linux. -enc_sockopt_key(socket, rcvlowat = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); +enc_sockopt_key(socket = L, rcvlowat = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); enc_sockopt_key(socket, rcvtimeo = Opt, _Dir, _D, _T, _P) -> not_supported(Opt); enc_sockopt_key(socket, reuseaddr = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SOCK_REUSEADDR; -enc_sockopt_key(socket, reuseport = Opt, _Dir, D, _T, _P) +enc_sockopt_key(socket = L, reuseport = Opt, _Dir, D, _T, _P) when ((D =:= inet) orelse (D =:= inet6)) -> - not_supported(Opt); -enc_sockopt_key(socket, rxq_ovfl = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); -enc_sockopt_key(socket, setfib = Opt, set = _Dir, _D, _T, _P) -> - not_supported(Opt); + not_supported({L, Opt}); +enc_sockopt_key(socket = L, rxq_ovfl = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(socket = L, setfib = Opt, set = _Dir, _D, _T, _P) -> + not_supported({L, Opt}); enc_sockopt_key(socket, sndbuf = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SOCK_SNDBUF; -enc_sockopt_key(socket, sndbufforce = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); +enc_sockopt_key(socket = L, sndbufforce = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); %% Not changeable on linux. -enc_sockopt_key(socket, sndlowat = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); -enc_sockopt_key(socket, sndtimeo = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); -enc_sockopt_key(socket, timestamp = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); -enc_sockopt_key(socket, UnknownOpt, _Dir, _D, _T, _P) -> - unknown(UnknownOpt); +enc_sockopt_key(socket = L, sndlowat = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(socket = L, sndtimeo = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(socket = L, timestamp = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(socket = L, UnknownOpt, _Dir, _D, _T, _P) -> + unknown({L, UnknownOpt}); %% +++ IP socket options +++ -enc_sockopt_key(ip, add_membership = Opt, set = _Dir, _D, _T, _P) -> - not_supported(Opt); -enc_sockopt_key(ip, add_source_membership = Opt, set = _Dir, _D, _T, _P) -> - not_supported(Opt); -enc_sockopt_key(ip, block_source = Opt, set = _Dir, _D, _T, _P) -> - not_supported(Opt); +enc_sockopt_key(ip = L, add_membership = Opt, set = _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ip = L, add_source_membership = Opt, set = _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ip = L, block_source = Opt, set = _Dir, _D, _T, _P) -> + not_supported({L, Opt}); %% FreeBSD only? %% Only respected on udp and raw ip (unless the hdrincl option has been set). -enc_sockopt_key(ip, dontfrag = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); -enc_sockopt_key(ip, drop_membership = Opt, set = _Dir, _D, _T, _P) -> - not_supported(Opt); -enc_sockopt_key(ip, drop_source_membership = Opt, set = _Dir, _D, _T, _P) -> - not_supported(Opt); +enc_sockopt_key(ip = L, dontfrag = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ip = L, drop_membership = Opt, set = _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ip = L, drop_source_membership = Opt, set = _Dir, _D, _T, _P) -> + not_supported({L, Opt}); %% Linux only? -enc_sockopt_key(ip, free_bind = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); -enc_sockopt_key(ip, hdrincl = Opt, _Dir, _D, raw = _T, _P) -> - not_supported(Opt); +enc_sockopt_key(ip = L, free_bind = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ip = L, hdrincl = Opt, _Dir, _D, raw = _T, _P) -> + not_supported({L, Opt}); %% FreeBSD only? -enc_sockopt_key(ip, minttl = Opt, _Dir, _D, raw = _T, _P) -> - not_supported(Opt); -enc_sockopt_key(ip, msfilter = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); -enc_sockopt_key(ip, mtu = Opt, get = _Dir, _D, _T, _P) -> - not_supported(Opt); -enc_sockopt_key(ip, mtu_discover = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); -enc_sockopt_key(ip, multicast_all = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); -enc_sockopt_key(ip, multicast_if = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); -enc_sockopt_key(ip, multicast_loop = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); -enc_sockopt_key(ip, multicast_ttl = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); -enc_sockopt_key(ip, nodefrag = Opt, _Dir, _D, raw = _T, _P) -> - not_supported(Opt); -enc_sockopt_key(ip, options = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); -enc_sockopt_key(ip, pktinfo = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); +enc_sockopt_key(ip = L, minttl = Opt, _Dir, _D, raw = _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ip = L, msfilter = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ip = L, mtu = Opt, get = _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ip = L, mtu_discover = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ip = L, multicast_all = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ip = L, multicast_if = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ip = L, multicast_loop = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ip = L, multicast_ttl = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ip = L, nodefrag = Opt, _Dir, _D, raw = _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ip = L, options = Opt, _Dir, _D, _T, _P) -> + not_supported({Opt, L}); +enc_sockopt_key(ip = L, pktinfo = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); %% This require special code for accessing the errors. %% via calling the recvmsg with the MSG_ERRQUEUE flag set, -enc_sockopt_key(ip, recverr = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); -enc_sockopt_key(ip, recvif = Opt, _Dir, _D, dgram = _T, _P) -> - not_supported(Opt); -enc_sockopt_key(ip, recvdstaddr = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); -enc_sockopt_key(ip, recvopts = Opt, _Dir, _D, T, _P) when (T =/= stream) -> - not_supported(Opt); -enc_sockopt_key(ip, recvorigdstaddr = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); +enc_sockopt_key(ip = L, recverr = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ip = L, recvif = Opt, _Dir, _D, dgram = _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ip = L, recvdstaddr = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ip = L, recvopts = Opt, _Dir, _D, T, _P) when (T =/= stream) -> + not_supported({L, Opt}); +enc_sockopt_key(ip = L, recvorigdstaddr = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); enc_sockopt_key(ip, recvtos = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_RECVTOS; -enc_sockopt_key(ip, recvttl = Opt, _Dir, _D, T, _P) when (T =/= stream) -> - not_supported(Opt); -enc_sockopt_key(ip, retopts = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); +enc_sockopt_key(ip = L, recvttl = Opt, _Dir, _D, T, _P) when (T =/= stream) -> + not_supported({L, Opt}); +enc_sockopt_key(ip = L, retopts = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); enc_sockopt_key(ip, router_alert = _Opt, _Dir, _D, raw = _T, _P) -> ?SOCKET_OPT_IP_ROUTER_ALERT; %% On FreeBSD it specifies that this option is only valid @@ -1999,49 +2041,49 @@ enc_sockopt_key(ip, router_alert = _Opt, _Dir, _D, raw = _T, _P) -> %% No such condition on linux (in the man page)... enc_sockopt_key(ip, tos = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_TOS; -enc_sockopt_key(ip, transparent = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); +enc_sockopt_key(ip = L, transparent = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); enc_sockopt_key(ip, ttl = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_TTL; -enc_sockopt_key(ip, unblock_source = Opt, set = _Dir, _D, _T, _P) -> - not_supported(Opt); -enc_sockopt_key(ip, UnknownOpt, _Dir, _D, _T, _P) -> - unknown(UnknownOpt); +enc_sockopt_key(ip = L, unblock_source = Opt, set = _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ip = L, UnknownOpt, _Dir, _D, _T, _P) -> + unknown({L, UnknownOpt}); %% IPv6 socket options enc_sockopt_key(ipv6, hoplimit = _Opt, _Dir, _D, T, _P) when (T =:= dgram) orelse (T =:= raw) -> ?SOCKET_OPT_IPV6_HOPLIMIT; -enc_sockopt_key(ipv6, UnknownOpt, _Dir, _D, _T, _P) -> - unknown(UnknownOpt); +enc_sockopt_key(ipv6 = L, UnknownOpt, _Dir, _D, _T, _P) -> + unknown({L, UnknownOpt}); %% TCP socket options %% There are other options that would be useful; info, %% but they are difficult to get portable... enc_sockopt_key(tcp, congestion = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_TCP_CONGESTION; -enc_sockopt_key(tcp, cork = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); +enc_sockopt_key(tcp = L, cork = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); enc_sockopt_key(tcp, maxseg = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_TCP_MAXSEG; enc_sockopt_key(tcp, nodelay = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_TCP_NODELAY; -enc_sockopt_key(tcp, UnknownOpt, _Dir, _D, _T, _P) -> - unknown(UnknownOpt); +enc_sockopt_key(tcp = L, UnknownOpt, _Dir, _D, _T, _P) -> + unknown({L, UnknownOpt}); %% UDP socket options enc_sockopt_key(udp, cork = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_UDP_CORK; -enc_sockopt_key(udp, UnknownOpt, _Dir, _D, _T, _P) -> - unknown(UnknownOpt); +enc_sockopt_key(udp = L, UnknownOpt, _Dir, _D, _T, _P) -> + unknown({L, UnknownOpt}); %% SCTP socket options enc_sockopt_key(sctp, autoclose = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SCTP_AUTOCLOSE; enc_sockopt_key(sctp, nodelay = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SCTP_NODELAY; -enc_sockopt_key(sctp, UnknownOpt, _Dir, _D, _T, _P) -> - unknown(UnknownOpt); +enc_sockopt_key(sctp = L, UnknownOpt, _Dir, _D, _T, _P) -> + unknown({L, UnknownOpt}); %% +++ "Native" socket options +++ enc_sockopt_key(Level, Opt, set = _Dir, _D, _T, _P) @@ -2158,6 +2200,16 @@ tdiff(T1, T2) -> +p(F, A) -> + p(get(sname), F, A). + +p(undefined, F, A) -> + p("***", F, A); +p(SName, F, A) -> + io:format("[~s,~p] " ++ F ++ "~n", [SName, self()|A]). + + + %% =========================================================================== %% %% Error functions -- cgit v1.2.3 From e39e25d84405e13ca0ce476e3ba473510e5548de Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 4 Jul 2018 15:18:18 +0200 Subject: [socket-nif] Fixed (dgram) recv Fixed handling of recvfrom (used by dgram sockets). Had forgot to do select(read) when we got block from the call to recvfrom. Argh! Also updated the (simple) test server and client to to be able to use udp (dgram+udp). OTP-14831 --- erts/preloaded/src/socket.erl | 122 +++++++++++++++++++++++++++--------------- 1 file changed, 79 insertions(+), 43 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index bf94271073..79b8471e08 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -37,7 +37,7 @@ accept/1, accept/2, send/2, send/3, send/4, - sendto/4, sendto/5, + sendto/3, sendto/4, sendto/5, %% sendmsg/4, %% writev/4, OR SENDV? It will be strange for recv then: recvv (instead of readv) @@ -50,7 +50,10 @@ shutdown/2, setopt/4, - getopt/3 + getopt/3, + + sockname/1, + peername/1 ]). -export_type([ @@ -204,11 +207,11 @@ reuseaddr | reuseport | rxq_ovfl | - sndlowat | - sndtimeo | setfib | sndbuf | sndbufforce | + sndlowat | + sndtimeo | timestamp | type. @@ -431,6 +434,7 @@ -define(SOCKET_SEND_FLAGS_DEFAULT, []). -define(SOCKET_SEND_TIMEOUT_DEFAULT, infinity). +-define(SOCKET_SENDTO_FLAGS_DEFAULT, []). -define(SOCKET_SENDTO_TIMEOUT_DEFAULT, ?SOCKET_SEND_TIMEOUT_DEFAULT). -define(SOCKET_RECV_FLAG_CMSG_CLOEXEC, 0). @@ -462,6 +466,7 @@ -define(SOCKET_OPT_SOCK_RCVBUF, 17). -define(SOCKET_OPT_SOCK_REUSEADDR, 21). -define(SOCKET_OPT_SOCK_SNDBUF, 27). +-define(SOCKET_OPT_SOCK_TYPE, 32). -define(SOCKET_OPT_IP_RECVTOS, 25). -define(SOCKET_OPT_IP_ROUTER_ALERT, 28). @@ -927,40 +932,43 @@ do_send(SockRef, Data, EFlags, Timeout) -> %% --------------------------------------------------------------------------- %% -sendto(Socket, Data, Flags, Dest) -> - sendto(Socket, Data, Flags, Dest, ?SOCKET_SENDTO_TIMEOUT_DEFAULT). +sendto(Socket, Data, Dest) -> + sendto(Socket, Data, Dest, ?SOCKET_SENDTO_FLAGS_DEFAULT). + +sendto(Socket, Data, Dest, Flags) -> + sendto(Socket, Data, Dest, Flags, ?SOCKET_SENDTO_TIMEOUT_DEFAULT). --spec sendto(Socket, Data, Flags, Dest, Timeout) -> +-spec sendto(Socket, Data, Dest, Flags, Timeout) -> ok | {error, Reason} when Socket :: socket(), Data :: binary(), - Flags :: send_flags(), Dest :: null | sockaddr(), + Flags :: send_flags(), Timeout :: timeout(), Reason :: term(). -sendto(Socket, Data, Flags, Dest, Timeout) when is_list(Data) -> +sendto(Socket, Data, Dest, Flags, Timeout) when is_list(Data) -> Bin = erlang:list_to_binary(Data), - sendto(Socket, Bin, Flags, Dest, Timeout); -sendto(#socket{ref = SockRef}, Data, Flags, Dest, Timeout) + sendto(Socket, Bin, Dest, Flags, Timeout); +sendto(#socket{ref = SockRef}, Data, Dest, Flags, Timeout) when is_binary(Data) andalso - is_list(Flags) andalso (Dest =:= null) andalso + is_list(Flags) andalso (is_integer(Timeout) orelse (Timeout =:= infinity)) -> EFlags = enc_send_flags(Flags), - do_sendto(SockRef, Data, EFlags, Dest, Timeout); -sendto(#socket{ref = SockRef}, Data, Flags, #{family := Fam} = Dest, Timeout) + do_sendto(SockRef, Data, Dest, EFlags, Timeout); +sendto(#socket{ref = SockRef}, Data, #{family := Fam} = Dest, Flags, Timeout) when is_binary(Data) andalso - is_list(Flags) andalso ((Fam =:= inet) orelse (Fam =:= inet6) orelse (Fam =:= local)) andalso + is_list(Flags) andalso (is_integer(Timeout) orelse (Timeout =:= infinity)) -> EFlags = enc_send_flags(Flags), - do_sendto(SockRef, Data, EFlags, Dest, Timeout). + do_sendto(SockRef, Data, Dest, EFlags, Timeout). -do_sendto(SockRef, Data, EFlags, Dest, Timeout) -> +do_sendto(SockRef, Data, Dest, EFlags, Timeout) -> TS = timestamp(Timeout), SendRef = make_ref(), - case nif_sendto(SockRef, SendRef, Data, EFlags, Dest) of + case nif_sendto(SockRef, SendRef, Data, Dest, EFlags) of ok -> %% We are done ok; @@ -970,10 +978,10 @@ do_sendto(SockRef, Data, EFlags, Dest, Timeout) -> receive {select, SockRef, SendRef, ready_output} when (Written > 0) -> <<_:Written/binary, Rest/binary>> = Data, - do_sendto(SockRef, Rest, EFlags, Dest, + do_sendto(SockRef, Rest, Dest, EFlags, next_timeout(TS, Timeout)); {select, SockRef, SendRef, ready_output} -> - do_sendto(SockRef, Data, EFlags, Dest, + do_sendto(SockRef, Data, Dest, EFlags, next_timeout(TS, Timeout)); {nif_abort, SendRef, Reason} -> @@ -988,7 +996,7 @@ do_sendto(SockRef, Data, EFlags, Dest, Timeout) -> {error, eagain} -> receive {select, SockRef, SendRef, ready_output} -> - do_sendto(SockRef, Data, EFlags, Dest, + do_sendto(SockRef, Data, Dest, EFlags, next_timeout(TS, Timeout)) after Timeout -> nif_cancel(SockRef, sendto, SendRef), @@ -1135,21 +1143,10 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) (is_integer(Timeout) andalso (Timeout > 0)) -> TS = timestamp(Timeout), RecvRef = make_ref(), - p("do_recv -> try read with" - "~n SockRef: ~p" - "~n RecvRef: ~p" - "~n Length: ~p" - "~n EFlags: ~p" - "~nwhen" - "~n Timeout: ~p (~p)", [SockRef, RecvRef, Length, EFlags, Timeout, TS]), case nif_recv(SockRef, RecvRef, Length, EFlags) of {ok, true = _Complete, Bin} when (size(Acc) =:= 0) -> - p("do_recv -> ok: complete (size(Acc) =:= 0)" - "~n size(Bin): ~p", [size(Bin)]), {ok, Bin}; {ok, true = _Complete, Bin} -> - p("do_recv -> ok: complete" - "~n size(Bin): ~p", [size(Bin)]), {ok, <>}; %% It depends on the amount of bytes we tried to read: @@ -1158,16 +1155,12 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) %% > 0 - We got a part of the message and we will be notified %% when there is more to read (a select message) {ok, false = _Complete, Bin} when (Length =:= 0) -> - p("do_recv -> ok: not-complete (Length =:= 0)" - "~n size(Bin): ~p", [size(Bin)]), do_recv(SockRef, RecvRef, Length, EFlags, <>, next_timeout(TS, Timeout)); {ok, false = _Completed, Bin} when (size(Acc) =:= 0) -> - p("do_recv -> ok: not-complete (size(Acc) =:= 0)" - "~n size(Bin): ~p", [size(Bin)]), %% We got the first chunk of it. %% We will be notified (select message) when there %% is more to read. @@ -1190,8 +1183,6 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) end; {ok, false = _Completed, Bin} -> - p("do_recv -> ok: not-complete" - "~n size(Bin): ~p", [size(Bin)]), %% We got a chunk of it! NewTimeout = next_timeout(TS, Timeout), receive @@ -1213,17 +1204,14 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) %% We return with the accumulated binary (if its non-empty) {error, eagain} when (Length =:= 0) andalso (size(Acc) > 0) -> - p("do_recv -> eagain (Length =:= 0)", []), {ok, Acc}; {error, eagain} -> - p("do_recv -> eagain", []), %% There is nothing just now, but we will be notified when there %% is something to read (a select message). NewTimeout = next_timeout(TS, Timeout), receive {select, SockRef, RecvRef, ready_input} -> - p("do_recv -> received select ready-input message", []), do_recv(SockRef, RecvRef, Length, EFlags, Acc, @@ -1317,16 +1305,21 @@ recvfrom(#socket{ref = SockRef}, BufSz, Flags, Timeout) do_recvfrom(SockRef, BufSz, EFlags, Timeout) -> TS = timestamp(Timeout), RecvRef = make_ref(), + p("recvfrom -> try recvfrom"), case nif_recvfrom(SockRef, RecvRef, BufSz, EFlags) of {ok, {_Source, _NewData}} = OK -> + p("recvfrom -> ok: " + "~n Source: ~p", [_Source]), OK; {error, eagain} -> + p("recvfrom -> eagain - wait for select ready-input"), %% There is nothing just now, but we will be notified when there %% is something to read (a select message). NewTimeout = next_timeout(TS, Timeout), receive {select, SockRef, RecvRef, ready_input} -> + p("recvfrom -> eagain - got select ready-input"), do_recvfrom(SockRef, BufSz, EFlags, next_timeout(TS, Timeout)); @@ -1339,7 +1332,8 @@ do_recvfrom(SockRef, BufSz, EFlags, Timeout) -> {error, timeout} end; - {error, _} = ERROR -> + {error, _Reason} = ERROR -> + p("recvfrom -> error: ~p", [_Reason]), ERROR end. @@ -1572,6 +1566,37 @@ getopt(#socket{info = Info, ref = SockRef}, Level, Key) -> +%% =========================================================================== +%% +%% sockname - return the current address of the socket. +%% +%% + +-spec sockname(Socket) -> {ok, SockAddr} | {error, Reason} when + Socket :: socket(), + SockAddr :: sockaddr(), + Reason :: term(). + +sockname(#socket{ref = SockRef}) -> + nif_sockname(SockRef). + + + +%% =========================================================================== +%% +%% peername - return the address of the peer *connected* to the socket. +%% +%% + +-spec peername(Socket) -> {ok, SockAddr} | {error, Reason} when + Socket :: socket(), + SockAddr :: sockaddr(), + Reason :: term(). + +peername(#socket{ref = SockRef}) -> + nif_peername(SockRef). + + %% =========================================================================== %% @@ -1970,6 +1995,8 @@ enc_sockopt_key(socket = L, sndtimeo = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(socket = L, timestamp = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); +enc_sockopt_key(socket = _L, type = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SOCK_TYPE; enc_sockopt_key(socket = L, UnknownOpt, _Dir, _D, _T, _P) -> unknown({L, UnknownOpt}); @@ -2200,6 +2227,9 @@ tdiff(T1, T2) -> +p(F) -> + p(F, []). + p(F, A) -> p(get(sname), F, A). @@ -2261,7 +2291,7 @@ nif_accept(_SRef, _Ref) -> nif_send(_SockRef, _SendRef, _Data, _Flags) -> erlang:error(badarg). -nif_sendto(_SRef, _SendRef, _Data, _Flags, _DestSockAddr) -> +nif_sendto(_SRef, _SendRef, _Data, _Dest, _Flags) -> erlang:error(badarg). nif_recv(_SRef, _RecvRef, _Length, _Flags) -> @@ -2288,3 +2318,9 @@ nif_setopt(_Ref, _IsEnc, _Lev, _Key, _Val) -> nif_getopt(_Ref, _IsEnc, _Lev, _Key) -> erlang:error(badarg). +nif_sockname(_Ref) -> + erlang:error(badarg). + +nif_peername(_Ref) -> + erlang:error(badarg). + -- cgit v1.2.3 From eacb95d88db1e1123d17288e71835b457bcf4016 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 4 Jul 2018 18:37:32 +0200 Subject: [socket-doc-nif] Updated documentation Updated the documentation of recv, recvfrom, send and sendto. Also added doc for functions peername and sockname. OTP-14831 --- erts/preloaded/src/socket.erl | 157 +++++++++++++++++++++++++++++++++--------- 1 file changed, 123 insertions(+), 34 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 79b8471e08..a1db295a79 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -88,7 +88,10 @@ udp_socket_option/0, sctp_socket_option/0, - ip_tos_flag/0 + ip_tos_flag/0, + + + msg_hdr/0 ]). %% We support only a subset of all domains. @@ -385,22 +388,23 @@ -type shutdown_how() :: read | write | read_write. +%% This is just a place-holder -record(msg_hdr, { - %% Optional address - %% On an unconnected socket this is used to specify the target - %% address for a datagram. - %% For a connected socket, this field should be specifiedset to []. - name :: list(), - - %% Scatter/gather array - iov :: [binary()], % iovec(), - - %% Ancillary (control) data - ctrl :: binary(), - - %% Unused - flags = [] :: list() + %% Optional address + %% On an unconnected socket this is used to specify the target + %% address for a datagram. + %% For a connected socket, this field should be specifiedset to []. + name :: list(), + + %% Scatter/gather array + iov :: [binary()], % iovec(), + + %% Ancillary (control) data + ctrl :: binary(), + + %% Unused + flags = [] :: list() }). -type msg_hdr() :: #msg_hdr{}. @@ -858,9 +862,25 @@ do_accept(LSockRef, SI, Timeout) -> %% send, sendto, sendmsg - send a message on a socket %% +-spec send(Socket, Data) -> ok | {error, Reason} when + Socket :: socket(), + Data :: iodata(), + Reason :: term(). + send(Socket, Data) -> send(Socket, Data, ?SOCKET_SEND_FLAGS_DEFAULT, ?SOCKET_SEND_TIMEOUT_DEFAULT). +-spec send(Socket, Data, Flags) -> ok | {error, Reason} when + Socket :: socket(), + Data :: iodata(), + Flags :: send_flags(), + Reason :: term() + ; (Socket, Data, Timeout) -> ok | {error, Reason} when + Socket :: socket(), + Data :: iodata(), + Timeout :: timeout(), + Reason :: term(). + send(Socket, Data, Flags) when is_list(Flags) -> send(Socket, Data, Flags, ?SOCKET_SEND_TIMEOUT_DEFAULT); send(Socket, Data, Timeout) -> @@ -932,14 +952,27 @@ do_send(SockRef, Data, EFlags, Timeout) -> %% --------------------------------------------------------------------------- %% +-spec sendto(Socket, Data, Dest) -> + ok | {error, Reason} when + Socket :: socket(), + Data :: binary(), + Dest :: null | sockaddr(), + Reason :: term(). + sendto(Socket, Data, Dest) -> sendto(Socket, Data, Dest, ?SOCKET_SENDTO_FLAGS_DEFAULT). +-spec sendto(Socket, Data, Dest, Flags) -> ok | {error, Reason} when + Socket :: socket(), + Data :: binary(), + Dest :: null | sockaddr(), + Flags :: send_flags(), + Reason :: term(). + sendto(Socket, Data, Dest, Flags) -> sendto(Socket, Data, Dest, Flags, ?SOCKET_SENDTO_TIMEOUT_DEFAULT). --spec sendto(Socket, Data, Dest, Flags, Timeout) -> - ok | {error, Reason} when +-spec sendto(Socket, Data, Dest, Flags, Timeout) -> ok | {error, Reason} when Socket :: socket(), Data :: binary(), Dest :: null | sockaddr(), @@ -1014,7 +1047,7 @@ do_sendto(SockRef, Data, Dest, EFlags, Timeout) -> %% -spec sendmsg(Socket, MsgHdr, Flags) -> ok | {error, Reason} when %% Socket :: socket(), -%% MsgHdr :: msg_header(), +%% MsgHdr :: msg_hdr(), %% Flags :: send_flags(), %% Reason :: term(). @@ -1106,14 +1139,38 @@ do_sendto(SockRef, Data, Dest, EFlags, Timeout) -> %% Flags - A list of "options" for the read. %% Timeout - Time-out in milliseconds. +-spec recv(Socket) -> {ok, Data} | {error, Reason} when + Socket :: socket(), + Data :: binary(), + Reason :: term(). + recv(Socket) -> recv(Socket, 0). +-spec recv(Socket, Length) -> {ok, Data} | {error, Reason} when + Socket :: socket(), + Length :: non_neg_integer(), + Data :: binary(), + Reason :: term(). + recv(Socket, Length) -> recv(Socket, Length, ?SOCKET_RECV_FLAGS_DEFAULT, ?SOCKET_RECV_TIMEOUT_DEFAULT). +-spec recv(Socket, Length, Flags) -> {ok, Data} | {error, Reason} when + Socket :: socket(), + Length :: non_neg_integer(), + Flags :: recv_flags(), + Data :: binary(), + Reason :: term() + ; (Socket, Length, Timeout) -> {ok, Data} | {error, Reason} when + Socket :: socket(), + Length :: non_neg_integer(), + Timeout :: timeout(), + Data :: binary(), + Reason :: term(). + recv(Socket, Length, Flags) when is_list(Flags) -> recv(Socket, Length, Flags, ?SOCKET_RECV_TIMEOUT_DEFAULT); recv(Socket, Length, Timeout) -> @@ -1270,14 +1327,51 @@ do_recv(_SockRef, _RecvRef, _Length, _EFlags, _Acc, _Timeout) -> %% is needed, possibly with a then adjusted buffer size. %% +-spec recvfrom(Socket) -> {ok, {Source, Data}} | {error, Reason} when + Socket :: socket(), + Source :: sockaddr() | undefined, + Data :: binary(), + Reason :: term(). + recvfrom(Socket) -> recvfrom(Socket, 0). +-spec recvfrom(Socket, BufSz) -> {ok, {Source, Data}} | {error, Reason} when + Socket :: socket(), + BufSz :: non_neg_integer(), + Source :: sockaddr() | undefined, + Data :: binary(), + Reason :: term(). + recvfrom(Socket, BufSz) -> recvfrom(Socket, BufSz, ?SOCKET_RECV_FLAGS_DEFAULT, ?SOCKET_RECV_TIMEOUT_DEFAULT). +-spec recvfrom(Socket, Flags, Timeout) -> + {ok, {Source, Data}} | {error, Reason} when + Socket :: socket(), + Flags :: recv_flags(), + Timeout :: timeout(), + Source :: sockaddr() | undefined, + Data :: binary(), + Reason :: term() + ; (Socket, BufSz, Flags) -> + {ok, {Source, Data}} | {error, Reason} when + Socket :: socket(), + BufSz :: non_neg_integer(), + Flags :: recv_flags(), + Source :: sockaddr() | undefined, + Data :: binary(), + Reason :: term() + ; (Socket, BufSz, Timeout) -> + {ok, {Source, Data}} | {error, Reason} when + Socket :: socket(), + BufSz :: non_neg_integer(), + Timeout :: timeout(), + Source :: sockaddr() | undefined, + Data :: binary(), + Reason :: term(). recvfrom(Socket, Flags, Timeout) when is_list(Flags) -> recvfrom(Socket, 0, Flags, Timeout); @@ -1286,7 +1380,8 @@ recvfrom(Socket, BufSz, Flags) when is_list(Flags) -> recvfrom(Socket, BufSz, Timeout) -> recvfrom(Socket, BufSz, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout). --spec recvfrom(Socket, BufSz, Flags, Timeout) -> {ok, {Source, Data}} | {error, Reason} when +-spec recvfrom(Socket, BufSz, Flags, Timeout) -> + {ok, {Source, Data}} | {error, Reason} when Socket :: socket(), BufSz :: non_neg_integer(), Flags :: recv_flags(), @@ -1305,21 +1400,16 @@ recvfrom(#socket{ref = SockRef}, BufSz, Flags, Timeout) do_recvfrom(SockRef, BufSz, EFlags, Timeout) -> TS = timestamp(Timeout), RecvRef = make_ref(), - p("recvfrom -> try recvfrom"), case nif_recvfrom(SockRef, RecvRef, BufSz, EFlags) of {ok, {_Source, _NewData}} = OK -> - p("recvfrom -> ok: " - "~n Source: ~p", [_Source]), OK; {error, eagain} -> - p("recvfrom -> eagain - wait for select ready-input"), %% There is nothing just now, but we will be notified when there %% is something to read (a select message). NewTimeout = next_timeout(TS, Timeout), receive {select, SockRef, RecvRef, ready_input} -> - p("recvfrom -> eagain - got select ready-input"), do_recvfrom(SockRef, BufSz, EFlags, next_timeout(TS, Timeout)); @@ -1333,7 +1423,6 @@ do_recvfrom(SockRef, BufSz, EFlags, Timeout) -> end; {error, _Reason} = ERROR -> - p("recvfrom -> error: ~p", [_Reason]), ERROR end. @@ -1345,7 +1434,7 @@ do_recvfrom(SockRef, BufSz, EFlags, Timeout) -> %% -spec recvmsg(Socket, [out] MsgHdr, Flags) -> {ok, Data} | {error, Reason} when %% Socket :: socket(), -%% MsgHdr :: msg_header(), +%% MsgHdr :: msg_hdr(), %% Flags :: recv_flags(), %% Data :: binary(), %% Reason :: term(). @@ -2227,16 +2316,16 @@ tdiff(T1, T2) -> -p(F) -> - p(F, []). +%% p(F) -> +%% p(F, []). -p(F, A) -> - p(get(sname), F, A). +%% p(F, A) -> +%% p(get(sname), F, A). -p(undefined, F, A) -> - p("***", F, A); -p(SName, F, A) -> - io:format("[~s,~p] " ++ F ++ "~n", [SName, self()|A]). +%% p(undefined, F, A) -> +%% p("***", F, A); +%% p(SName, F, A) -> +%% io:format("[~s,~p] " ++ F ++ "~n", [SName, self()|A]). -- cgit v1.2.3 From 44cfb3d222ba4d20607af7cc654746f84ece3989 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 19 Jun 2018 12:20:21 +0200 Subject: [socket-nif] Add doc for net module and some cleanup Added doc for the net module. Also some socket-nif cleanup. OTP-14831 --- erts/preloaded/src/net.erl | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/net.erl b/erts/preloaded/src/net.erl index 6abfc22d3f..01b12696e9 100644 --- a/erts/preloaded/src/net.erl +++ b/erts/preloaded/src/net.erl @@ -69,17 +69,10 @@ idna_use_std3_ascii_rules. -type name_info() :: #{host := string(), service := string()}. -%% -record(name_info, {host, service}). -%% -type name_info() :: #name_info{}. -type address_info() :: #{family := socket:domain(), socktype := socket:type(), protocol := socket:protocol(), address := socket:sockaddr()}. -%% -record(address_info, {family :: socket:domain(), -%% socktype :: socket:type(), -%% protocol :: socket:protocol(), -%% addr :: undefined | socket:in_sockaddr() | string()}). -%% -type address_info() :: #address_info{}. -type network_interface_name() :: string(). -type network_interface_index() :: non_neg_integer(). @@ -218,6 +211,11 @@ getaddrinfo(Host) when is_list(Host) -> ; (undefined, Service) -> {ok, Info} | {error, Reason} when Service :: string(), Info :: [address_info()], + Reason :: term() + ; (Host, Service) -> {ok, Info} | {error, Reason} when + Host :: string(), + Service :: string(), + Info :: [address_info()], Reason :: term(). getaddrinfo(Host, Service) @@ -236,8 +234,8 @@ getaddrinfo(Host, Service) %% -spec if_name2index(Name) -> {ok, Idx} | {error, Reason} when - Name :: string(), - Idx :: non_neg_integer(), + Name :: network_interface_name(), + Idx :: network_interface_index(), Reason :: term(). if_name2index(If) when is_list(If) -> @@ -247,14 +245,14 @@ if_name2index(If) when is_list(If) -> %% =========================================================================== %% -%% if_index2name - Mappings between network interface names and indexes: +%% if_index2name - Mappings between network interface index and names: %% idx -> name %% %% -spec if_index2name(Idx) -> {ok, Name} | {error, Reason} when - Idx :: non_neg_integer(), - Name :: string(), + Idx :: network_interface_index(), + Name :: network_interface_name(), Reason :: term(). if_index2name(Idx) when is_integer(Idx) -> @@ -270,8 +268,8 @@ if_index2name(Idx) when is_integer(Idx) -> -spec if_names() -> Names | {error, Reason} when Names :: [{Idx, If}], - Idx :: non_neg_integer(), - If :: string(), + Idx :: network_interface_index(), + If :: network_interface_name(), Reason :: term(). if_names() -> -- cgit v1.2.3 From d45e3bf3dfeda0e849b07a9a7a19e50a52b04c35 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 9 Jul 2018 14:10:35 +0200 Subject: [socket-nif] Add support for socket (level socket) options domain and protocol Make it possible to *get* the socket options domain and protocol (in addition to type). OTP-14831 --- erts/preloaded/src/socket.erl | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index a1db295a79..4932e301fb 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -193,6 +193,7 @@ broadcast | busy_poll | debug | + domain | dontroute | error | keepalive | @@ -203,6 +204,7 @@ peek_off | peek_cred | priority | + protocol | rcvbuf | rcvbufforce | rcvlowat | @@ -463,14 +465,16 @@ -define(SOCKET_OPT_OTP_CTRL_PROC, 2). -define(SOCKET_OPT_SOCK_BROADCAST, 4). --define(SOCKET_OPT_SOCK_DONTROUTE, 7). --define(SOCKET_OPT_SOCK_KEEPALIVE, 9). --define(SOCKET_OPT_SOCK_LINGER, 10). --define(SOCKET_OPT_SOCK_PRIORITY, 16). --define(SOCKET_OPT_SOCK_RCVBUF, 17). --define(SOCKET_OPT_SOCK_REUSEADDR, 21). --define(SOCKET_OPT_SOCK_SNDBUF, 27). --define(SOCKET_OPT_SOCK_TYPE, 32). +-define(SOCKET_OPT_SOCK_DOMAIN, 7). +-define(SOCKET_OPT_SOCK_DONTROUTE, 8). +-define(SOCKET_OPT_SOCK_KEEPALIVE, 10). +-define(SOCKET_OPT_SOCK_LINGER, 11). +-define(SOCKET_OPT_SOCK_PRIORITY, 17). +-define(SOCKET_OPT_SOCK_PROTOCOL, 18). +-define(SOCKET_OPT_SOCK_RCVBUF, 19). +-define(SOCKET_OPT_SOCK_REUSEADDR, 23). +-define(SOCKET_OPT_SOCK_SNDBUF, 29). +-define(SOCKET_OPT_SOCK_TYPE, 34). -define(SOCKET_OPT_IP_RECVTOS, 25). -define(SOCKET_OPT_IP_ROUTER_ALERT, 28). @@ -2032,6 +2036,8 @@ enc_sockopt_key(socket = L, busy_poll = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(socket = L, debug = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); +enc_sockopt_key(socket, domain = _Opt, get = _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SOCK_DOMAIN; enc_sockopt_key(socket, dontroute = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SOCK_DONTROUTE; enc_sockopt_key(socket = L, error = Opt, get = _Dir, _D, _T, _P) -> @@ -2055,6 +2061,8 @@ enc_sockopt_key(socket = L, peek_cred = Opt, get = _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(socket, priority = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SOCK_PRIORITY; +enc_sockopt_key(socket, protocol = _Opt, get = _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SOCK_PROTOCOL; enc_sockopt_key(socket, rcvbuf = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SOCK_RCVBUF; enc_sockopt_key(socket = L, rcvbufforce = Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 7ed2af8dcfd62a051686ac1e1326fbeca0b18334 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 9 Jul 2018 14:54:57 +0200 Subject: [socket-nif] Add support for socket (level socket) option acceptconn The socket (level socket) option acceptconn is now supported (for getopt). OTP-14831 --- erts/preloaded/src/socket.erl | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 4932e301fb..f22f9e5072 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -202,7 +202,7 @@ oobinline | passcred | peek_off | - peek_cred | + peekcred | priority | protocol | rcvbuf | @@ -464,17 +464,28 @@ -define(SOCKET_OPT_OTP_IOW, 1). -define(SOCKET_OPT_OTP_CTRL_PROC, 2). +-define(SOCKET_OPT_SOCK_ACCEPTCONN, 1). +%% -define(SOCKET_OPT_SOCK_ACCEPTFILTER, 2). +%% -define(SOCKET_OPT_SOCK_BINDTODEVICE, 3). -define(SOCKET_OPT_SOCK_BROADCAST, 4). +%% -define(SOCKET_OPT_SOCK_BUSY_POLL, 5). +%% -define(SOCKET_OPT_SOCK_DEBUG, 6). -define(SOCKET_OPT_SOCK_DOMAIN, 7). -define(SOCKET_OPT_SOCK_DONTROUTE, 8). +%% -define(SOCKET_OPT_SOCK_ERROR, 9). -define(SOCKET_OPT_SOCK_KEEPALIVE, 10). -define(SOCKET_OPT_SOCK_LINGER, 11). --define(SOCKET_OPT_SOCK_PRIORITY, 17). --define(SOCKET_OPT_SOCK_PROTOCOL, 18). --define(SOCKET_OPT_SOCK_RCVBUF, 19). --define(SOCKET_OPT_SOCK_REUSEADDR, 23). --define(SOCKET_OPT_SOCK_SNDBUF, 29). --define(SOCKET_OPT_SOCK_TYPE, 34). +%% -define(SOCKET_OPT_SOCK_MARK, 12). +%% -define(SOCKET_OPT_SOCK_OOBINLLINE, 13). +%% -define(SOCKET_OPT_SOCK_PASSCRED, 14). +%% -define(SOCKET_OPT_SOCK_PEEK_OFF, 16). +%% -define(SOCKET_OPT_SOCK_PEEKCRED, 17). +-define(SOCKET_OPT_SOCK_PRIORITY, 18). +-define(SOCKET_OPT_SOCK_PROTOCOL, 19). +-define(SOCKET_OPT_SOCK_RCVBUF, 20). +-define(SOCKET_OPT_SOCK_REUSEADDR, 24). +-define(SOCKET_OPT_SOCK_SNDBUF, 30). +-define(SOCKET_OPT_SOCK_TYPE, 35). -define(SOCKET_OPT_IP_RECVTOS, 25). -define(SOCKET_OPT_IP_ROUTER_ALERT, 28). @@ -2021,10 +2032,10 @@ enc_sockopt_key(otp = L, Opt, _, _, _, _) -> not_supported({L, Opt}); %% +++ SOCKET socket options +++ -enc_sockopt_key(socket = L, acceptconn = Opt, get = _Dir, _D, _T, _P) -> +enc_sockopt_key(socket = _L, acceptconn = _Opt, get = _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SOCK_ACCEPTCONN; +enc_sockopt_key(socket = L, acceptfilter = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(socket, acceptfilter = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); %% Before linux 3.8, this socket option could be set. %% Size of buffer for name: IFNAMSZ %% So, we let the implementation decide. @@ -2057,7 +2068,7 @@ enc_sockopt_key(socket = L, passcred = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(socket = L, peek_off = Opt, _Dir, local = _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(socket = L, peek_cred = Opt, get = _Dir, _D, _T, _P) -> +enc_sockopt_key(socket = L, peekcred = Opt, get = _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(socket, priority = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SOCK_PRIORITY; -- cgit v1.2.3 From 6031321a78e3df5304f555ba356a4482469e7d56 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 9 Jul 2018 15:28:09 +0200 Subject: [socket-nif] Add support for socket (level socket) option debug The socket option (level socket) debug is now supported. To actually set this option, the user must have the proper "authority" (CAP_NET_ADMIN capability or an effective user ID of 0). OTP-14831 --- erts/preloaded/src/socket.erl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index f22f9e5072..66ae9c7fd8 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -469,7 +469,7 @@ %% -define(SOCKET_OPT_SOCK_BINDTODEVICE, 3). -define(SOCKET_OPT_SOCK_BROADCAST, 4). %% -define(SOCKET_OPT_SOCK_BUSY_POLL, 5). -%% -define(SOCKET_OPT_SOCK_DEBUG, 6). +-define(SOCKET_OPT_SOCK_DEBUG, 6). -define(SOCKET_OPT_SOCK_DOMAIN, 7). -define(SOCKET_OPT_SOCK_DONTROUTE, 8). %% -define(SOCKET_OPT_SOCK_ERROR, 9). @@ -1831,6 +1831,8 @@ enc_setopt_value(otp = L, Opt, V, _D, _T, _P) -> enc_setopt_value(socket, broadcast, V, _D, _T, _P) when is_boolean(V) -> V; +enc_setopt_value(socket, debug, V, _D, _T, _P) when is_integer(V) -> + V; enc_setopt_value(socket, keepalive, V, _D, _T, _P) when is_boolean(V) -> V; enc_setopt_value(socket, linger, abort, D, T, P) -> @@ -2045,8 +2047,8 @@ enc_sockopt_key(socket, broadcast = _Opt, _Dir, _D, dgram = _T, _P) -> ?SOCKET_OPT_SOCK_BROADCAST; enc_sockopt_key(socket = L, busy_poll = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(socket = L, debug = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(socket = _L, debug = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SOCK_DEBUG; enc_sockopt_key(socket, domain = _Opt, get = _Dir, _D, _T, _P) -> ?SOCKET_OPT_SOCK_DOMAIN; enc_sockopt_key(socket, dontroute = _Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 5329726352f8b983e419b6206a85c15cc8836676 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 9 Jul 2018 18:41:07 +0200 Subject: [socket-nif] Add support for socket (level socket) option peek_off The socket option (level socket) peek_off is now supported. OTP-14831 --- erts/preloaded/src/socket.erl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 66ae9c7fd8..8f489bd681 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -478,7 +478,7 @@ %% -define(SOCKET_OPT_SOCK_MARK, 12). %% -define(SOCKET_OPT_SOCK_OOBINLLINE, 13). %% -define(SOCKET_OPT_SOCK_PASSCRED, 14). -%% -define(SOCKET_OPT_SOCK_PEEK_OFF, 16). +-define(SOCKET_OPT_SOCK_PEEK_OFF, 16). %% -define(SOCKET_OPT_SOCK_PEEKCRED, 17). -define(SOCKET_OPT_SOCK_PRIORITY, 18). -define(SOCKET_OPT_SOCK_PROTOCOL, 19). @@ -1840,6 +1840,8 @@ enc_setopt_value(socket, linger, abort, D, T, P) -> enc_setopt_value(socket, linger, {OnOff, Secs} = V, _D, _T, _P) when is_boolean(OnOff) andalso is_integer(Secs) andalso (Secs >= 0) -> V; +enc_setopt_value(socket, peek_off, V, _D, _T, _P) when is_integer(V) -> + V; enc_setopt_value(socket, priority, V, _D, _T, _P) when is_integer(V) -> V; enc_setopt_value(socket, rcvbuf, V, _D, _T, _P) when is_integer(V) -> @@ -2068,8 +2070,8 @@ enc_sockopt_key(socket, oobinline = Opt, _Dir, _D, _T, _P) -> not_supported(Opt); enc_sockopt_key(socket = L, passcred = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(socket = L, peek_off = Opt, _Dir, local = _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(socket = _L, peek_off = _Opt, _Dir, local = _D, _T, _P) -> + ?SOCKET_OPT_SOCK_PEEK_OFF; enc_sockopt_key(socket = L, peekcred = Opt, get = _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(socket, priority = _Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 9110ba256099e6fa55461fc4ca90da5ec4b2966b Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 10 Jul 2018 11:28:11 +0200 Subject: [socket-nif] Add support for socket (level socket) option oobinline Added support for socket level socket option oobinline (both get and set). OTP-14831 --- erts/preloaded/src/socket.erl | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 8f489bd681..278a3f17ad 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -476,16 +476,26 @@ -define(SOCKET_OPT_SOCK_KEEPALIVE, 10). -define(SOCKET_OPT_SOCK_LINGER, 11). %% -define(SOCKET_OPT_SOCK_MARK, 12). -%% -define(SOCKET_OPT_SOCK_OOBINLLINE, 13). +-define(SOCKET_OPT_SOCK_OOBINLINE, 13). %% -define(SOCKET_OPT_SOCK_PASSCRED, 14). --define(SOCKET_OPT_SOCK_PEEK_OFF, 16). -%% -define(SOCKET_OPT_SOCK_PEEKCRED, 17). --define(SOCKET_OPT_SOCK_PRIORITY, 18). --define(SOCKET_OPT_SOCK_PROTOCOL, 19). --define(SOCKET_OPT_SOCK_RCVBUF, 20). --define(SOCKET_OPT_SOCK_REUSEADDR, 24). --define(SOCKET_OPT_SOCK_SNDBUF, 30). --define(SOCKET_OPT_SOCK_TYPE, 35). +-define(SOCKET_OPT_SOCK_PEEK_OFF, 15). +%% -define(SOCKET_OPT_SOCK_PEEKCRED, 16). +-define(SOCKET_OPT_SOCK_PRIORITY, 17). +-define(SOCKET_OPT_SOCK_PROTOCOL, 18). +-define(SOCKET_OPT_SOCK_RCVBUF, 19). +%% -define(SOCKET_OPT_SOCK_RCVBUFFORCE, 20). +%% -define(SOCKET_OPT_SOCK_RCVLOWAT, 21). +%% -define(SOCKET_OPT_SOCK_RCVTIMEO, 22). +-define(SOCKET_OPT_SOCK_REUSEADDR, 23). +%% -define(SOCKET_OPT_SOCK_REUSEPORT, 24). +%% -define(SOCKET_OPT_SOCK_RXQ_OVFL, 25). +%% -define(SOCKET_OPT_SOCK_SETFIB, 26). +-define(SOCKET_OPT_SOCK_SNDBUF, 27). +%% -define(SOCKET_OPT_SOCK_SNDBUFFORCE, 28). +%% -define(SOCKET_OPT_SOCK_SNDLOWAT, 29). +%% -define(SOCKET_OPT_SOCK_SNDTIMEO, 30). +%% -define(SOCKET_OPT_SOCK_TIMESTAMP, 31). +-define(SOCKET_OPT_SOCK_TYPE, 32). -define(SOCKET_OPT_IP_RECVTOS, 25). -define(SOCKET_OPT_IP_ROUTER_ALERT, 28). @@ -1840,6 +1850,8 @@ enc_setopt_value(socket, linger, abort, D, T, P) -> enc_setopt_value(socket, linger, {OnOff, Secs} = V, _D, _T, _P) when is_boolean(OnOff) andalso is_integer(Secs) andalso (Secs >= 0) -> V; +enc_setopt_value(socket, oobinline, V, _D, _T, _P) when is_boolean(V) -> + V; enc_setopt_value(socket, peek_off, V, _D, _T, _P) when is_integer(V) -> V; enc_setopt_value(socket, priority, V, _D, _T, _P) when is_integer(V) -> @@ -2066,8 +2078,8 @@ enc_sockopt_key(socket, linger = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SOCK_LINGER; enc_sockopt_key(socket = L, mark = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(socket, oobinline = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); +enc_sockopt_key(socket = _L, oobinline = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SOCK_OOBINLINE; enc_sockopt_key(socket = L, passcred = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(socket = _L, peek_off = _Opt, _Dir, local = _D, _T, _P) -> -- cgit v1.2.3 From 8936b71f13b37559600afb5536ff7d7878b9ab0e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 10 Jul 2018 13:42:28 +0200 Subject: [socket-nif] Add support for socket (level ip) option multicast_loop Added support for the IP option MULTICAST_LOOP. OTP-14831 --- erts/preloaded/src/socket.erl | 127 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 110 insertions(+), 17 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 278a3f17ad..d2ed733b3c 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -460,9 +460,9 @@ -define(SOCKET_OPT_LEVEL_UDP, 5). -define(SOCKET_OPT_LEVEL_SCTP, 6). --define(SOCKET_OPT_OTP_DEBUG, 0). --define(SOCKET_OPT_OTP_IOW, 1). --define(SOCKET_OPT_OTP_CTRL_PROC, 2). +-define(SOCKET_OPT_OTP_DEBUG, 1). +-define(SOCKET_OPT_OTP_IOW, 2). +-define(SOCKET_OPT_OTP_CTRL_PROC, 3). -define(SOCKET_OPT_SOCK_ACCEPTCONN, 1). %% -define(SOCKET_OPT_SOCK_ACCEPTFILTER, 2). @@ -497,22 +497,112 @@ %% -define(SOCKET_OPT_SOCK_TIMESTAMP, 31). -define(SOCKET_OPT_SOCK_TYPE, 32). --define(SOCKET_OPT_IP_RECVTOS, 25). --define(SOCKET_OPT_IP_ROUTER_ALERT, 28). --define(SOCKET_OPT_IP_TOS, 30). --define(SOCKET_OPT_IP_TTL, 32). - --define(SOCKET_OPT_IPV6_HOPLIMIT, 12). - --define(SOCKET_OPT_TCP_CONGESTION, 0). +%% -define(SOCKET_OPT_IP_ADD_MEMBERSHIP, 1). +%% -define(SOCKET_OPT_IP_ADD_SOURCE_MEMBERSHIP, 2). +%% -define(SOCKET_OPT_IP_BLOCK_SOURCE, 3). +%% -define(SOCKET_OPT_IP_DONT_FRAG, 4). +%% -define(SOCKET_OPT_IP_DROP_MEMBERSHIP, 5). +%% -define(SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP, 6). +%% -define(SOCKET_OPT_IP_FREEBIND, 7). +%% -define(SOCKET_OPT_IP_HDRINCL, 8). +%% -define(SOCKET_OPT_IP_MINTTL, 9). +%% -define(SOCKET_OPT_IP_MSFILTER, 10). +%% -define(SOCKET_OPT_IP_MTU, 11). +%% -define(SOCKET_OPT_IP_MTU_DISCOVER, 12). +%% -define(SOCKET_OPT_IP_MULTICAST_ALL, 13). +%% -define(SOCKET_OPT_IP_MULTICAST_IF, 14). +-define(SOCKET_OPT_IP_MULTICAST_LOOP, 15). +%% -define(SOCKET_OPT_IP_MULTICAST_TTL, 16). +%% -define(SOCKET_OPT_IP_NODEFRAG, 17). +%% -define(SOCKET_OPT_IP_OPTIONS, 18). +%% -define(SOCKET_OPT_IP_PKTINFO, 19). +%% -define(SOCKET_OPT_IP_RECVERR, 20). +%% -define(SOCKET_OPT_IP_RECVIF, 21). +%% -define(SOCKET_OPT_IP_RECVDSTADDR, 22). +%% -define(SOCKET_OPT_IP_RECVOPTS, 23). +%% -define(SOCKET_OPT_IP_RECVORIGDSTADDR, 24). +-define(SOCKET_OPT_IP_RECVTOS, 25). +%% -define(SOCKET_OPT_IP_RECVTTL, 26). +%% -define(SOCKET_OPT_IP_RETOPTS, 27). +-define(SOCKET_OPT_IP_ROUTER_ALERT, 28). +%% -define(SOCKET_OPT_IP_SNDSRCADDR, 29). +-define(SOCKET_OPT_IP_TOS, 30). +%% -define(SOCKET_OPT_IP_TRANSPARENT, 31). +-define(SOCKET_OPT_IP_TTL, 32). +-define(SOCKET_OPT_IP_UNBLOCK_SOURCE, 33). + +%% -define(SOCKET_OPT_IPV6_ADDFORM, 1). +%% -define(SOCKET_OPT_IPV6_ADD_MEMBERSHIP, 2). +%% -define(SOCKET_OPT_IPV6_AUTHHDR, 3). +%% -define(SOCKET_OPT_IPV6_AUTH_LEVEL, 4). +%% -define(SOCKET_OPT_IPV6_CHECKSUM, 5). +%% -define(SOCKET_OPT_IPV6_DROP_MEMBERSHIP, 6). +%% -define(SOCKET_OPT_IPV6_DSTOPTS, 7). +%% -define(SOCKET_OPT_IPV6_ESP_TRANS_LEVEL, 8). +%% -define(SOCKET_OPT_IPV6_ESP_NETWORK_LEVEL, 9). +%% -define(SOCKET_OPT_IPV6_FAITH, 10). +%% -define(SOCKET_OPT_IPV6_FLOWINFO, 11). +-define(SOCKET_OPT_IPV6_HOPLIMIT, 12). +%% -define(SOCKET_OPT_IPV6_HOPOPTS, 13). +%% -define(SOCKET_OPT_IPV6_IPCOMP_LEVEL, 14). +%% -define(SOCKET_OPT_IPV6_JOIN_GROUP, 15). +%% -define(SOCKET_OPT_IPV6_LEAVE_GROUP, 16). +%% -define(SOCKET_OPT_IPV6_MTU, 17). +%% -define(SOCKET_OPT_IPV6_MTU_DISCOVER, 18). +%% -define(SOCKET_OPT_IPV6_MULTICAST_HOPS, 19). +%% -define(SOCKET_OPT_IPV6_MULTICAST_IF, 20). +%% -define(SOCKET_OPT_IPV6_MULTICAST_LOOP, 21). +%% -define(SOCKET_OPT_IPV6_PORTRANGE, 22). +%% -define(SOCKET_OPT_IPV6_PKTINFO, 23). +%% -define(SOCKET_OPT_IPV6_PKTOPTIONS, 24). +%% -define(SOCKET_OPT_IPV6_RECVERR, 25). +%% -define(SOCKET_OPT_IPV6_RECVPKTINFO, 26). +%% -define(SOCKET_OPT_IPV6_RECVTCLASS, 27). +%% -define(SOCKET_OPT_IPV6_ROUTER_ALERT, 28). +%% -define(SOCKET_OPT_IPV6_RTHDR, 29). +%% -define(SOCKET_OPT_IPV6_TCLASS, 30). +%% -define(SOCKET_OPT_IPV6_UNICAST_HOPS, 31). +%% -define(SOCKET_OPT_IPV6_USE_MIN_MTU, 32). +%% -define(SOCKET_OPT_IPV6_V6ONLY, 33). + +-define(SOCKET_OPT_TCP_CONGESTION, 1). -define(SOCKET_OPT_TCP_CORK, 2). -define(SOCKET_OPT_TCP_MAXSEG, 3). -define(SOCKET_OPT_TCP_NODELAY, 4). --define(SOCKET_OPT_UDP_CORK, 0). - --define(SOCKET_OPT_SCTP_AUTOCLOSE, 7). --define(SOCKET_OPT_SCTP_NODELAY, 22). +-define(SOCKET_OPT_UDP_CORK, 1). + +%% -define(SOCKET_OPT_SCTP_ADAPTION_LAYER, 1). +%% -define(SOCKET_OPT_SCTP_ASSOCINFO, 2). +%% -define(SOCKET_OPT_SCTP_AUTH_ACTIVE_KEY, 3). +%% -define(SOCKET_OPT_SCTP_AUTH_ASCONF, 4). +%% -define(SOCKET_OPT_SCTP_AUTH_CHUNK, 5). +%% -define(SOCKET_OPT_SCTP_AUTH_KEY, 6). +%% -define(SOCKET_OPT_SCTP_AUTH_DELETE_KEY, 7). +-define(SOCKET_OPT_SCTP_AUTOCLOSE, 8). +%% -define(SOCKET_OPT_SCTP_CONTEXT, 9). +%% -define(SOCKET_OPT_SCTP_DEFAULT_SEND_PARAMS, 10). +%% -define(SOCKET_OPT_SCTP_DELAYED_ACK_TIME, 11). +%% -define(SOCKET_OPT_SCTP_DISABLE_FRAGMENTS, 12). +%% -define(SOCKET_OPT_SCTP_HMAC_IDENT, 13). +%% -define(SOCKET_OPT_SCTP_EXPLICIT_EOR, 14). +%% -define(SOCKET_OPT_SCTP_FRAGMENT_INTERLEAVE, 15). +%% -define(SOCKET_OPT_SCTP_GET_PEER_ADDR_INFO, 16). +%% -define(SOCKET_OPT_SCTP_INITMSG, 17). +%% -define(SOCKET_OPT_SCTP_I_WANT_MAPPED_V4_ADDR, 18). +%% -define(SOCKET_OPT_SCTP_LOCAL_AUTH_CHUNKS, 19). +%% -define(SOCKET_OPT_SCTP_MAXSEG, 20). +%% -define(SOCKET_OPT_SCTP_MAXBURST, 21). +-define(SOCKET_OPT_SCTP_NODELAY, 22). +%% -define(SOCKET_OPT_SCTP_PARTIAL_DELIVERY_POINT, 23). +%% -define(SOCKET_OPT_SCTP_PEER_ADDR_PARAMS, 24). +%% -define(SOCKET_OPT_SCTP_PEER_AUTH_CHUNKS, 25). +%% -define(SOCKET_OPT_SCTP_PRIMARY_ADDR, 26). +%% -define(SOCKET_OPT_SCTP_RESET_STREAMS, 27). +%% -define(SOCKET_OPT_SCTP_RTOINFO, 28). +%% -define(SOCKET_OPT_SCTP_SET_PEER_PRIMARY_ADDR, 29). +%% -define(SOCKET_OPT_SCTP_STATUS, 30). +%% -define(SOCKET_OPT_SCTP_USE_EXT_RECVINFO, 31). -define(SOCKET_SHUTDOWN_HOW_READ, 0). -define(SOCKET_SHUTDOWN_HOW_WRITE, 1). @@ -1865,6 +1955,9 @@ enc_setopt_value(socket, sndbuf, V, _D, _T, _P) when is_integer(V) -> enc_setopt_value(socket = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); +enc_setopt_value(ip, multicast_loop, V, _D, _T, _P) + when is_boolean(V) -> + V; enc_setopt_value(ip, recvtos, V, _D, _T, _P) when is_boolean(V) -> V; @@ -2157,8 +2250,8 @@ enc_sockopt_key(ip = L, multicast_all = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ip = L, multicast_if = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(ip = L, multicast_loop = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, multicast_loop = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IP_MULTICAST_LOOP; enc_sockopt_key(ip = L, multicast_ttl = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ip = L, nodefrag = Opt, _Dir, _D, raw = _T, _P) -> -- cgit v1.2.3 From 59586c412340d24188f78730c5b2c45db772c8ca Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 10 Jul 2018 14:01:00 +0200 Subject: [socket-nif] Add support for socket (level ip) option multicast_ttl Added support for the IP option MULTICAST_TTL. OTP-14831 --- erts/preloaded/src/socket.erl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index d2ed733b3c..f243559ceb 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -512,7 +512,7 @@ %% -define(SOCKET_OPT_IP_MULTICAST_ALL, 13). %% -define(SOCKET_OPT_IP_MULTICAST_IF, 14). -define(SOCKET_OPT_IP_MULTICAST_LOOP, 15). -%% -define(SOCKET_OPT_IP_MULTICAST_TTL, 16). +-define(SOCKET_OPT_IP_MULTICAST_TTL, 16). %% -define(SOCKET_OPT_IP_NODEFRAG, 17). %% -define(SOCKET_OPT_IP_OPTIONS, 18). %% -define(SOCKET_OPT_IP_PKTINFO, 19). @@ -1958,6 +1958,9 @@ enc_setopt_value(socket = L, Opt, V, _D, _T, _P) -> enc_setopt_value(ip, multicast_loop, V, _D, _T, _P) when is_boolean(V) -> V; +enc_setopt_value(ip, multicast_ttl, V, _D, _T, _P) + when is_integer(V) andalso (0 =< V) andalso (V =< 255) -> + V; enc_setopt_value(ip, recvtos, V, _D, _T, _P) when is_boolean(V) -> V; @@ -2252,8 +2255,8 @@ enc_sockopt_key(ip = L, multicast_if = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ip = _L, multicast_loop = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_MULTICAST_LOOP; -enc_sockopt_key(ip = L, multicast_ttl = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, multicast_ttl = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IP_MULTICAST_TTL; enc_sockopt_key(ip = L, nodefrag = Opt, _Dir, _D, raw = _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ip = L, options = Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 8118227d80fc41efac23d30a5601fdfadb75931f Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 10 Jul 2018 17:41:40 +0200 Subject: [socket-nif] Add support for socket (level ip) option multicast_if Added support for the IP option MULTICAST_IF. OTP-14831 --- erts/preloaded/src/socket.erl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index f243559ceb..997a4ac225 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -510,7 +510,7 @@ %% -define(SOCKET_OPT_IP_MTU, 11). %% -define(SOCKET_OPT_IP_MTU_DISCOVER, 12). %% -define(SOCKET_OPT_IP_MULTICAST_ALL, 13). -%% -define(SOCKET_OPT_IP_MULTICAST_IF, 14). +-define(SOCKET_OPT_IP_MULTICAST_IF, 14). -define(SOCKET_OPT_IP_MULTICAST_LOOP, 15). -define(SOCKET_OPT_IP_MULTICAST_TTL, 16). %% -define(SOCKET_OPT_IP_NODEFRAG, 17). @@ -1955,6 +1955,9 @@ enc_setopt_value(socket, sndbuf, V, _D, _T, _P) when is_integer(V) -> enc_setopt_value(socket = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); +enc_setopt_value(ip, multicast_if, V, _D, _T, _P) + when (V =:= any) orelse (is_tuple(V) andalso (size(V) =:= 4)) -> + V; enc_setopt_value(ip, multicast_loop, V, _D, _T, _P) when is_boolean(V) -> V; @@ -2251,8 +2254,8 @@ enc_sockopt_key(ip = L, mtu_discover = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ip = L, multicast_all = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(ip = L, multicast_if = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, multicast_if = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IP_MULTICAST_IF; enc_sockopt_key(ip = _L, multicast_loop = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_MULTICAST_LOOP; enc_sockopt_key(ip = _L, multicast_ttl = _Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 04524794d5e1f80a33f48201e5c14de8c396c30e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 11 Jul 2018 11:20:46 +0200 Subject: [socket-nif] Add support for socket (level ip) options [add|drop]_membership Added support for the socket options (level ip) add_membership and drop_membership. OTP-14831 --- erts/preloaded/src/socket.erl | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 997a4ac225..3e7679daa1 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -89,6 +89,7 @@ sctp_socket_option/0, ip_tos_flag/0, + ip_mreq/0, msg_hdr/0 @@ -134,6 +135,20 @@ %% %% +%% This type is used when requesting to become member of a multicast +%% group with a call to setopt. Example: +%% +%% socket:setopt(Socket, ip, add_membership, #{multiaddr => Addr, +%% interface => any}). +%% +%% Its also used when removing from a multicast group. Example: +%% +%% socket:setopt(Socket, ip, drop_membership, #{multiaddr => Addr, +%% interface => any}). +%% +-type ip_mreq() :: #{multiaddr := ip4_address(), + interface := any | ip4_address()}. + -type sockaddr_un() :: #{family := local, path := binary() | string()}. -type sockaddr_in4() :: #{family := inet, @@ -497,11 +512,11 @@ %% -define(SOCKET_OPT_SOCK_TIMESTAMP, 31). -define(SOCKET_OPT_SOCK_TYPE, 32). -%% -define(SOCKET_OPT_IP_ADD_MEMBERSHIP, 1). +-define(SOCKET_OPT_IP_ADD_MEMBERSHIP, 1). %% -define(SOCKET_OPT_IP_ADD_SOURCE_MEMBERSHIP, 2). %% -define(SOCKET_OPT_IP_BLOCK_SOURCE, 3). %% -define(SOCKET_OPT_IP_DONT_FRAG, 4). -%% -define(SOCKET_OPT_IP_DROP_MEMBERSHIP, 5). +-define(SOCKET_OPT_IP_DROP_MEMBERSHIP, 5). %% -define(SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP, 6). %% -define(SOCKET_OPT_IP_FREEBIND, 7). %% -define(SOCKET_OPT_IP_HDRINCL, 8). @@ -1955,6 +1970,16 @@ enc_setopt_value(socket, sndbuf, V, _D, _T, _P) when is_integer(V) -> enc_setopt_value(socket = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); +enc_setopt_value(ip, add_membership, #{multiaddr := M, + interface := IF} = V, _D, _T, _P) + when (is_tuple(M) andalso (size(M) =:= 4)) andalso + ((IF =:= any) orelse (is_tuple(IF) andalso (size(IF) =:= 4))) -> + V; +enc_setopt_value(ip, drop_membership, #{multiaddr := M, + interface := IF} = V, _D, _T, _P) + when (is_tuple(M) andalso (size(M) =:= 4)) andalso + ((IF =:= any) orelse (is_tuple(IF) andalso (size(IF) =:= 4))) -> + V; enc_setopt_value(ip, multicast_if, V, _D, _T, _P) when (V =:= any) orelse (is_tuple(V) andalso (size(V) =:= 4)) -> V; @@ -2224,8 +2249,8 @@ enc_sockopt_key(socket = L, UnknownOpt, _Dir, _D, _T, _P) -> unknown({L, UnknownOpt}); %% +++ IP socket options +++ -enc_sockopt_key(ip = L, add_membership = Opt, set = _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, add_membership = _Opt, set = _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IP_ADD_MEMBERSHIP; enc_sockopt_key(ip = L, add_source_membership = Opt, set = _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ip = L, block_source = Opt, set = _Dir, _D, _T, _P) -> @@ -2234,8 +2259,8 @@ enc_sockopt_key(ip = L, block_source = Opt, set = _Dir, _D, _T, _P) -> %% Only respected on udp and raw ip (unless the hdrincl option has been set). enc_sockopt_key(ip = L, dontfrag = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(ip = L, drop_membership = Opt, set = _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, drop_membership = _Opt, set = _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IP_DROP_MEMBERSHIP; enc_sockopt_key(ip = L, drop_source_membership = Opt, set = _Dir, _D, _T, _P) -> not_supported({L, Opt}); %% Linux only? -- cgit v1.2.3 From 4d29dde851fb30d86c194961f93e148198ae456d Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 11 Jul 2018 11:53:31 +0200 Subject: [socket-nif] Add value type check for the dontroute (socket) option --- erts/preloaded/src/socket.erl | 2 ++ 1 file changed, 2 insertions(+) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 3e7679daa1..a4efc2e38c 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -1948,6 +1948,8 @@ enc_setopt_value(socket, broadcast, V, _D, _T, _P) when is_boolean(V) -> V; enc_setopt_value(socket, debug, V, _D, _T, _P) when is_integer(V) -> V; +enc_setopt_value(socket, dontroute, V, _D, _T, _P) when is_boolean(V) -> + V; enc_setopt_value(socket, keepalive, V, _D, _T, _P) when is_boolean(V) -> V; enc_setopt_value(socket, linger, abort, D, T, P) -> -- cgit v1.2.3 From e5a5cb1025270c265baeda89dd4cd13a1417a262 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 11 Jul 2018 18:20:06 +0200 Subject: [socket-nif] Add support for socket (level ip) option mtu Added support for the IP option MTU. OTP-14831 --- erts/preloaded/src/socket.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index a4efc2e38c..d6b2289b69 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -515,14 +515,14 @@ -define(SOCKET_OPT_IP_ADD_MEMBERSHIP, 1). %% -define(SOCKET_OPT_IP_ADD_SOURCE_MEMBERSHIP, 2). %% -define(SOCKET_OPT_IP_BLOCK_SOURCE, 3). -%% -define(SOCKET_OPT_IP_DONT_FRAG, 4). +%% -define(SOCKET_OPT_IP_DONTFRAG, 4). % Windows? MTU_DISCOVER... -define(SOCKET_OPT_IP_DROP_MEMBERSHIP, 5). %% -define(SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP, 6). %% -define(SOCKET_OPT_IP_FREEBIND, 7). %% -define(SOCKET_OPT_IP_HDRINCL, 8). %% -define(SOCKET_OPT_IP_MINTTL, 9). %% -define(SOCKET_OPT_IP_MSFILTER, 10). -%% -define(SOCKET_OPT_IP_MTU, 11). +-define(SOCKET_OPT_IP_MTU, 11). %% -define(SOCKET_OPT_IP_MTU_DISCOVER, 12). %% -define(SOCKET_OPT_IP_MULTICAST_ALL, 13). -define(SOCKET_OPT_IP_MULTICAST_IF, 14). @@ -2275,8 +2275,8 @@ enc_sockopt_key(ip = L, minttl = Opt, _Dir, _D, raw = _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ip = L, msfilter = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(ip = L, mtu = Opt, get = _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, mtu = _Opt, get = _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IP_MTU; enc_sockopt_key(ip = L, mtu_discover = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ip = L, multicast_all = Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From e0f27afac1cb3e3a5567a05081d0cf307c154b0d Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 12 Jul 2018 10:24:44 +0200 Subject: [socket-nif] Add support for socket (level ip) option mtu_discover Added support for the IP option MTU_DISCOVER. OTP-14831 --- erts/preloaded/src/socket.erl | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index d6b2289b69..5338f0d988 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -90,6 +90,7 @@ ip_tos_flag/0, ip_mreq/0, + ip_pmtudisc/0, msg_hdr/0 @@ -149,6 +150,8 @@ -type ip_mreq() :: #{multiaddr := ip4_address(), interface := any | ip4_address()}. +-type ip_pmtudisc() :: want | dont | do | probe. + -type sockaddr_un() :: #{family := local, path := binary() | string()}. -type sockaddr_in4() :: #{family := inet, @@ -523,7 +526,7 @@ %% -define(SOCKET_OPT_IP_MINTTL, 9). %% -define(SOCKET_OPT_IP_MSFILTER, 10). -define(SOCKET_OPT_IP_MTU, 11). -%% -define(SOCKET_OPT_IP_MTU_DISCOVER, 12). +-define(SOCKET_OPT_IP_MTU_DISCOVER, 12). %% -define(SOCKET_OPT_IP_MULTICAST_ALL, 13). -define(SOCKET_OPT_IP_MULTICAST_IF, 14). -define(SOCKET_OPT_IP_MULTICAST_LOOP, 15). @@ -1982,6 +1985,13 @@ enc_setopt_value(ip, drop_membership, #{multiaddr := M, when (is_tuple(M) andalso (size(M) =:= 4)) andalso ((IF =:= any) orelse (is_tuple(IF) andalso (size(IF) =:= 4))) -> V; +enc_setopt_value(ip, mtu_discover, V, _D, _T, _P) + when (V =:= want) orelse + (V =:= dont) orelse + (V =:= do) orelse + (V =:= probe) orelse + is_integer(V) -> + V; enc_setopt_value(ip, multicast_if, V, _D, _T, _P) when (V =:= any) orelse (is_tuple(V) andalso (size(V) =:= 4)) -> V; @@ -2277,8 +2287,8 @@ enc_sockopt_key(ip = L, msfilter = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ip = _L, mtu = _Opt, get = _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_MTU; -enc_sockopt_key(ip = L, mtu_discover = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, mtu_discover = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IP_MTU_DISCOVER; enc_sockopt_key(ip = L, multicast_all = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ip = _L, multicast_if = _Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From ea680e88b967440b2ecd925321ebb5dd48461608 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 12 Jul 2018 10:42:19 +0200 Subject: [socket-nif] Add support for socket (level ip) option multicast_all Added support for the IP option MULTICAST_ALL. OTP-14831 --- erts/preloaded/src/socket.erl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 5338f0d988..643f59467b 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -527,7 +527,7 @@ %% -define(SOCKET_OPT_IP_MSFILTER, 10). -define(SOCKET_OPT_IP_MTU, 11). -define(SOCKET_OPT_IP_MTU_DISCOVER, 12). -%% -define(SOCKET_OPT_IP_MULTICAST_ALL, 13). +-define(SOCKET_OPT_IP_MULTICAST_ALL, 13). -define(SOCKET_OPT_IP_MULTICAST_IF, 14). -define(SOCKET_OPT_IP_MULTICAST_LOOP, 15). -define(SOCKET_OPT_IP_MULTICAST_TTL, 16). @@ -1992,6 +1992,9 @@ enc_setopt_value(ip, mtu_discover, V, _D, _T, _P) (V =:= probe) orelse is_integer(V) -> V; +enc_setopt_value(ip, multicast_all, V, _D, _T, _P) + when is_boolean(V) -> + V; enc_setopt_value(ip, multicast_if, V, _D, _T, _P) when (V =:= any) orelse (is_tuple(V) andalso (size(V) =:= 4)) -> V; @@ -2289,8 +2292,8 @@ enc_sockopt_key(ip = _L, mtu = _Opt, get = _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_MTU; enc_sockopt_key(ip = _L, mtu_discover = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_MTU_DISCOVER; -enc_sockopt_key(ip = L, multicast_all = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, multicast_all = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IP_MULTICAST_ALL; enc_sockopt_key(ip = _L, multicast_if = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_MULTICAST_IF; enc_sockopt_key(ip = _L, multicast_loop = _Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 0122d2cafdb6f44c221796f1a6b2a5188dfb153d Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 12 Jul 2018 11:04:38 +0200 Subject: [socket-nif] Add support for socket (level ip) option nodefrag Added support for the IP option NODEFRAG. OTP-14831 --- erts/preloaded/src/socket.erl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 643f59467b..3077a794a5 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -531,7 +531,7 @@ -define(SOCKET_OPT_IP_MULTICAST_IF, 14). -define(SOCKET_OPT_IP_MULTICAST_LOOP, 15). -define(SOCKET_OPT_IP_MULTICAST_TTL, 16). -%% -define(SOCKET_OPT_IP_NODEFRAG, 17). +-define(SOCKET_OPT_IP_NODEFRAG, 17). %% -define(SOCKET_OPT_IP_OPTIONS, 18). %% -define(SOCKET_OPT_IP_PKTINFO, 19). %% -define(SOCKET_OPT_IP_RECVERR, 20). @@ -2004,6 +2004,9 @@ enc_setopt_value(ip, multicast_loop, V, _D, _T, _P) enc_setopt_value(ip, multicast_ttl, V, _D, _T, _P) when is_integer(V) andalso (0 =< V) andalso (V =< 255) -> V; +enc_setopt_value(ip, nodefrag, V, _D, _T, _P) + when is_boolean(V) -> + V; enc_setopt_value(ip, recvtos, V, _D, _T, _P) when is_boolean(V) -> V; @@ -2300,8 +2303,8 @@ enc_sockopt_key(ip = _L, multicast_loop = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_MULTICAST_LOOP; enc_sockopt_key(ip = _L, multicast_ttl = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_MULTICAST_TTL; -enc_sockopt_key(ip = L, nodefrag = Opt, _Dir, _D, raw = _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, nodefrag = _Opt, _Dir, _D, raw = _T, _P) -> + ?SOCKET_OPT_IP_NODEFRAG; enc_sockopt_key(ip = L, options = Opt, _Dir, _D, _T, _P) -> not_supported({Opt, L}); enc_sockopt_key(ip = L, pktinfo = Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From ca81f1d1602cf994fca9fcd61e892c76e4e2742c Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 12 Jul 2018 11:22:53 +0200 Subject: [socket-nif] Add support for socket (level ip) option recvttl Added support for the IP option RECVTTL. OTP-14831 --- erts/preloaded/src/socket.erl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 3077a794a5..d54c8d5f33 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -540,7 +540,7 @@ %% -define(SOCKET_OPT_IP_RECVOPTS, 23). %% -define(SOCKET_OPT_IP_RECVORIGDSTADDR, 24). -define(SOCKET_OPT_IP_RECVTOS, 25). -%% -define(SOCKET_OPT_IP_RECVTTL, 26). +-define(SOCKET_OPT_IP_RECVTTL, 26). %% -define(SOCKET_OPT_IP_RETOPTS, 27). -define(SOCKET_OPT_IP_ROUTER_ALERT, 28). %% -define(SOCKET_OPT_IP_SNDSRCADDR, 29). @@ -2010,6 +2010,9 @@ enc_setopt_value(ip, nodefrag, V, _D, _T, _P) enc_setopt_value(ip, recvtos, V, _D, _T, _P) when is_boolean(V) -> V; +enc_setopt_value(ip, recvttl, V, _D, _T, _P) + when is_boolean(V) -> + V; enc_setopt_value(ip, router_alert, V, _D, _T, _P) when is_integer(V) -> V; @@ -2323,8 +2326,8 @@ enc_sockopt_key(ip = L, recvorigdstaddr = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ip, recvtos = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_RECVTOS; -enc_sockopt_key(ip = L, recvttl = Opt, _Dir, _D, T, _P) when (T =/= stream) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, recvttl = _Opt, _Dir, _D, T, _P) when (T =/= stream) -> + ?SOCKET_OPT_IP_RECVTTL; enc_sockopt_key(ip = L, retopts = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ip, router_alert = _Opt, _Dir, _D, raw = _T, _P) -> -- cgit v1.2.3 From 3d045788e8033e2e76396900358976ac92fb184a Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 12 Jul 2018 12:43:41 +0200 Subject: [socket-nif] Add support for socket (level ip) option(s) sources Added support for the IP options: ADD_SOURCE_MEMBERSHIP, DROP_SOURCE_MEMBERSHIP, BLOCK_SOURCE and UNBLOCK_SOURCE. OTP-14831 --- erts/preloaded/src/socket.erl | 70 ++++++++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 17 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index d54c8d5f33..7efe91146b 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -90,6 +90,7 @@ ip_tos_flag/0, ip_mreq/0, + ip_mreq_source/0, ip_pmtudisc/0, @@ -149,6 +150,13 @@ %% -type ip_mreq() :: #{multiaddr := ip4_address(), interface := any | ip4_address()}. +%% -type ip_mreqn() :: #{multiaddr := ip4_address(), +%% address := any | ip4_address(), +%% ifindex := integer()}. + +-type ip_mreq_source() :: #{multiaddr := ip4_address(), + interface := ip4_address(), + sourceaddr := ip4_address()}. -type ip_pmtudisc() :: want | dont | do | probe. @@ -516,11 +524,11 @@ -define(SOCKET_OPT_SOCK_TYPE, 32). -define(SOCKET_OPT_IP_ADD_MEMBERSHIP, 1). -%% -define(SOCKET_OPT_IP_ADD_SOURCE_MEMBERSHIP, 2). -%% -define(SOCKET_OPT_IP_BLOCK_SOURCE, 3). +-define(SOCKET_OPT_IP_ADD_SOURCE_MEMBERSHIP, 2). +-define(SOCKET_OPT_IP_BLOCK_SOURCE, 3). %% -define(SOCKET_OPT_IP_DONTFRAG, 4). % Windows? MTU_DISCOVER... -define(SOCKET_OPT_IP_DROP_MEMBERSHIP, 5). -%% -define(SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP, 6). +-define(SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP, 6). %% -define(SOCKET_OPT_IP_FREEBIND, 7). %% -define(SOCKET_OPT_IP_HDRINCL, 8). %% -define(SOCKET_OPT_IP_MINTTL, 9). @@ -1977,14 +1985,35 @@ enc_setopt_value(socket = L, Opt, V, _D, _T, _P) -> enc_setopt_value(ip, add_membership, #{multiaddr := M, interface := IF} = V, _D, _T, _P) - when (is_tuple(M) andalso (size(M) =:= 4)) andalso + when (is_tuple(M) andalso (size(M) =:= 4)) andalso ((IF =:= any) orelse (is_tuple(IF) andalso (size(IF) =:= 4))) -> V; -enc_setopt_value(ip, drop_membership, #{multiaddr := M, +enc_setopt_value(ip, add_source_membership, #{multiaddr := MA, + interface := IF, + sourceaddr := SA} = V, _D, _T, _P) + when (is_tuple(MA) andalso (size(MA) =:= 4)) andalso + (is_tuple(IF) andalso (size(IF) =:= 4)) andalso + (is_tuple(SA) andalso (size(SA) =:= 4)) -> + V; +enc_setopt_value(ip, block_source, #{multiaddr := MA, + interface := IF, + sourceaddr := SA} = V, _D, _T, _P) + when (is_tuple(MA) andalso (size(MA) =:= 4)) andalso + (is_tuple(IF) andalso (size(IF) =:= 4)) andalso + (is_tuple(SA) andalso (size(SA) =:= 4)) -> + V; +enc_setopt_value(ip, drop_membership, #{multiaddr := MA, interface := IF} = V, _D, _T, _P) - when (is_tuple(M) andalso (size(M) =:= 4)) andalso + when (is_tuple(MA) andalso (size(MA) =:= 4)) andalso ((IF =:= any) orelse (is_tuple(IF) andalso (size(IF) =:= 4))) -> V; +enc_setopt_value(ip, drop_source_membership, #{multiaddr := MA, + interface := IF, + sourceaddr := SA} = V, _D, _T, _P) + when (is_tuple(MA) andalso (size(MA) =:= 4)) andalso + (is_tuple(IF) andalso (size(IF) =:= 4)) andalso + (is_tuple(SA) andalso (size(SA) =:= 4)) -> + V; enc_setopt_value(ip, mtu_discover, V, _D, _T, _P) when (V =:= want) orelse (V =:= dont) orelse @@ -2017,15 +2046,22 @@ enc_setopt_value(ip, router_alert, V, _D, _T, _P) when is_integer(V) -> V; enc_setopt_value(ip, tos, V, _D, _T, _P) - when (V =:= lowdelay) orelse - (V =:= throughput) orelse + when (V =:= lowdelay) orelse + (V =:= throughput) orelse (V =:= reliability) orelse - (V =:= mincost) orelse + (V =:= mincost) orelse is_integer(V) -> V; enc_setopt_value(ip, ttl, V, _D, _T, _P) when is_integer(V) -> V; +enc_setopt_value(ip, unblock_source, #{multiaddr := MA, + interface := IF, + sourceaddr := SA} = V, _D, _T, _P) + when (is_tuple(MA) andalso (size(MA) =:= 4)) andalso + (is_tuple(IF) andalso (size(IF) =:= 4)) andalso + (is_tuple(SA) andalso (size(SA) =:= 4)) -> + V; enc_setopt_value(ip = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); @@ -2272,18 +2308,18 @@ enc_sockopt_key(socket = L, UnknownOpt, _Dir, _D, _T, _P) -> %% +++ IP socket options +++ enc_sockopt_key(ip = _L, add_membership = _Opt, set = _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_ADD_MEMBERSHIP; -enc_sockopt_key(ip = L, add_source_membership = Opt, set = _Dir, _D, _T, _P) -> - not_supported({L, Opt}); -enc_sockopt_key(ip = L, block_source = Opt, set = _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, add_source_membership = _Opt, set = _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IP_ADD_SOURCE_MEMBERSHIP; +enc_sockopt_key(ip = _L, block_source = _Opt, set = _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IP_BLOCK_SOURCE; %% FreeBSD only? %% Only respected on udp and raw ip (unless the hdrincl option has been set). enc_sockopt_key(ip = L, dontfrag = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ip = _L, drop_membership = _Opt, set = _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_DROP_MEMBERSHIP; -enc_sockopt_key(ip = L, drop_source_membership = Opt, set = _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, drop_source_membership = _Opt, set = _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP; %% Linux only? enc_sockopt_key(ip = L, free_bind = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); @@ -2341,8 +2377,8 @@ enc_sockopt_key(ip = L, transparent = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ip, ttl = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_TTL; -enc_sockopt_key(ip = L, unblock_source = Opt, set = _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, unblock_source = _Opt, set = _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IP_UNBLOCK_SOURCE; enc_sockopt_key(ip = L, UnknownOpt, _Dir, _D, _T, _P) -> unknown({L, UnknownOpt}); -- cgit v1.2.3 From 31ed3b1d9cff6171432459bf2de2b761e987ac97 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 12 Jul 2018 17:00:34 +0200 Subject: [socket-nif] Add support for socket (level ip) open recvif Added support for the IP option RECVIF. OTP-14831 --- erts/preloaded/src/socket.erl | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 7efe91146b..f70c45b42e 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -543,7 +543,7 @@ %% -define(SOCKET_OPT_IP_OPTIONS, 18). %% -define(SOCKET_OPT_IP_PKTINFO, 19). %% -define(SOCKET_OPT_IP_RECVERR, 20). -%% -define(SOCKET_OPT_IP_RECVIF, 21). +-define(SOCKET_OPT_IP_RECVIF, 21). %% -define(SOCKET_OPT_IP_RECVDSTADDR, 22). %% -define(SOCKET_OPT_IP_RECVOPTS, 23). %% -define(SOCKET_OPT_IP_RECVORIGDSTADDR, 24). @@ -2036,6 +2036,9 @@ enc_setopt_value(ip, multicast_ttl, V, _D, _T, _P) enc_setopt_value(ip, nodefrag, V, _D, _T, _P) when is_boolean(V) -> V; +enc_setopt_value(ip, recvif, V, _D, _T, _P) + when is_boolean(V) -> + V; enc_setopt_value(ip, recvtos, V, _D, _T, _P) when is_boolean(V) -> V; @@ -2352,8 +2355,9 @@ enc_sockopt_key(ip = L, pktinfo = Opt, _Dir, _D, _T, _P) -> %% via calling the recvmsg with the MSG_ERRQUEUE flag set, enc_sockopt_key(ip = L, recverr = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(ip = L, recvif = Opt, _Dir, _D, dgram = _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, recvif = _Opt, _Dir, _D, T, _P) + when (T =:= dgram) orelse (T =:= raw) -> + ?SOCKET_OPT_IP_RECVIF; enc_sockopt_key(ip = L, recvdstaddr = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ip = L, recvopts = Opt, _Dir, _D, T, _P) when (T =/= stream) -> -- cgit v1.2.3 From 2151895b4eb4cb3172ba7597477fd83eb85b444a Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 12 Jul 2018 17:30:59 +0200 Subject: [socket-nif] Add support for socket (level ip) option minttl Added support for the IP option MINTTL. OTP-14831 --- erts/preloaded/src/socket.erl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index f70c45b42e..c8238e9a1a 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -531,7 +531,7 @@ -define(SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP, 6). %% -define(SOCKET_OPT_IP_FREEBIND, 7). %% -define(SOCKET_OPT_IP_HDRINCL, 8). -%% -define(SOCKET_OPT_IP_MINTTL, 9). +-define(SOCKET_OPT_IP_MINTTL, 9). %% -define(SOCKET_OPT_IP_MSFILTER, 10). -define(SOCKET_OPT_IP_MTU, 11). -define(SOCKET_OPT_IP_MTU_DISCOVER, 12). @@ -2014,6 +2014,8 @@ enc_setopt_value(ip, drop_source_membership, #{multiaddr := MA, (is_tuple(IF) andalso (size(IF) =:= 4)) andalso (is_tuple(SA) andalso (size(SA) =:= 4)) -> V; +enc_setopt_value(ip, minttl, V, _D, _T, _P) when is_integer(V) -> + V; enc_setopt_value(ip, mtu_discover, V, _D, _T, _P) when (V =:= want) orelse (V =:= dont) orelse @@ -2329,8 +2331,8 @@ enc_sockopt_key(ip = L, free_bind = Opt, _Dir, _D, _T, _P) -> enc_sockopt_key(ip = L, hdrincl = Opt, _Dir, _D, raw = _T, _P) -> not_supported({L, Opt}); %% FreeBSD only? -enc_sockopt_key(ip = L, minttl = Opt, _Dir, _D, raw = _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, minttl = _Opt, _Dir, _D, raw = _T, _P) -> + ?SOCKET_OPT_IP_MINTTL; enc_sockopt_key(ip = L, msfilter = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ip = _L, mtu = _Opt, get = _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 3a2c48b2740f08d3582028fd2f8d626009aa911e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 12 Jul 2018 21:17:14 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option(s) [add|drop]_membership Added support for the IPv6 options ADD_MEMBERSHIP, DROP_MEMBERSHIP OTP-14831 --- erts/preloaded/src/socket.erl | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index c8238e9a1a..8d377d808e 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -92,6 +92,7 @@ ip_mreq/0, ip_mreq_source/0, ip_pmtudisc/0, + ipv6_mreq/0, msg_hdr/0 @@ -160,6 +161,9 @@ -type ip_pmtudisc() :: want | dont | do | probe. +-type ipv6_mreq() :: #{multiaddr := ip6_address(), + interface := non_neg_integer()}. + -type sockaddr_un() :: #{family := local, path := binary() | string()}. -type sockaddr_in4() :: #{family := inet, @@ -526,7 +530,7 @@ -define(SOCKET_OPT_IP_ADD_MEMBERSHIP, 1). -define(SOCKET_OPT_IP_ADD_SOURCE_MEMBERSHIP, 2). -define(SOCKET_OPT_IP_BLOCK_SOURCE, 3). -%% -define(SOCKET_OPT_IP_DONTFRAG, 4). % Windows? MTU_DISCOVER... +%% -define(SOCKET_OPT_IP_DONTFRAG, 4). -define(SOCKET_OPT_IP_DROP_MEMBERSHIP, 5). -define(SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP, 6). %% -define(SOCKET_OPT_IP_FREEBIND, 7). @@ -558,11 +562,11 @@ -define(SOCKET_OPT_IP_UNBLOCK_SOURCE, 33). %% -define(SOCKET_OPT_IPV6_ADDFORM, 1). -%% -define(SOCKET_OPT_IPV6_ADD_MEMBERSHIP, 2). +-define(SOCKET_OPT_IPV6_ADD_MEMBERSHIP, 2). %% -define(SOCKET_OPT_IPV6_AUTHHDR, 3). %% -define(SOCKET_OPT_IPV6_AUTH_LEVEL, 4). %% -define(SOCKET_OPT_IPV6_CHECKSUM, 5). -%% -define(SOCKET_OPT_IPV6_DROP_MEMBERSHIP, 6). +-define(SOCKET_OPT_IPV6_DROP_MEMBERSHIP, 6). %% -define(SOCKET_OPT_IPV6_DSTOPTS, 7). %% -define(SOCKET_OPT_IPV6_ESP_TRANS_LEVEL, 8). %% -define(SOCKET_OPT_IPV6_ESP_NETWORK_LEVEL, 9). @@ -1983,9 +1987,9 @@ enc_setopt_value(socket, sndbuf, V, _D, _T, _P) when is_integer(V) -> enc_setopt_value(socket = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); -enc_setopt_value(ip, add_membership, #{multiaddr := M, +enc_setopt_value(ip, add_membership, #{multiaddr := MA, interface := IF} = V, _D, _T, _P) - when (is_tuple(M) andalso (size(M) =:= 4)) andalso + when (is_tuple(MA) andalso (size(MA) =:= 4)) andalso ((IF =:= any) orelse (is_tuple(IF) andalso (size(IF) =:= 4))) -> V; enc_setopt_value(ip, add_source_membership, #{multiaddr := MA, @@ -2070,6 +2074,16 @@ enc_setopt_value(ip, unblock_source, #{multiaddr := MA, enc_setopt_value(ip = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); +enc_setopt_value(ipv6, add_membership, #{multiaddr := MA, + interface := IF} = V, _D, _T, _P) + when ((is_tuple(MA) andalso (size(MA) =:= 8)) andalso + (is_integer(IF) andalso (IF >= 0))) -> + V; +enc_setopt_value(ipv6, drop_membership, #{multiaddr := MA, + interface := IF} = V, _D, _T, _P) + when ((is_tuple(MA) andalso (size(MA) =:= 8)) andalso + (is_integer(IF) andalso (IF >= 0))) -> + V; enc_setopt_value(ipv6, hoplimit, V, _D, T, _P) when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) -> V; @@ -2389,6 +2403,10 @@ enc_sockopt_key(ip = L, UnknownOpt, _Dir, _D, _T, _P) -> unknown({L, UnknownOpt}); %% IPv6 socket options +enc_sockopt_key(ipv6, add_membership = _Opt, set = _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IPV6_ADD_MEMBERSHIP; +enc_sockopt_key(ipv6, drop_membership = _Opt, set = _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IPV6_DROP_MEMBERSHIP; enc_sockopt_key(ipv6, hoplimit = _Opt, _Dir, _D, T, _P) when (T =:= dgram) orelse (T =:= raw) -> ?SOCKET_OPT_IPV6_HOPLIMIT; -- cgit v1.2.3 From 5e0a36abaa984358f617541b102b4e4cbb112956 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 13 Jul 2018 14:42:34 +0200 Subject: [socket-nif] Add support for socket (level socket) option bindtodevice Added support for socket level socket option BINDTODEVICE. OTP-14831 --- erts/preloaded/src/socket.erl | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 8d377d808e..d4bf55511c 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -366,15 +366,15 @@ status | use_ext_recvinfo. -%% -type plain_socket_options() :: integer(). -%% -type sockopts() :: otp_socket_options() | -%% socket_options() | -%% ip_socket_options() | -%% ipv6_socket_options() | -%% tcp_socket_options() | -%% udp_socket_options() | -%% sctp_socket_options() | -%% plain_socket_options(). +%% -type plain_socket_option() :: integer(). +%% -type sockopt() :: otp_socket_option() | +%% socket_option() | +%% ip_socket_option() | +%% ipv6_socket_option() | +%% tcp_socket_option() | +%% udp_socket_option() | +%% sctp_socket_option() | +%% plain_socket_option(). %% If the integer value is used its up to the caller to ensure its valid! -type ip_tos_flag() :: lowdeley | @@ -496,7 +496,7 @@ -define(SOCKET_OPT_SOCK_ACCEPTCONN, 1). %% -define(SOCKET_OPT_SOCK_ACCEPTFILTER, 2). -%% -define(SOCKET_OPT_SOCK_BINDTODEVICE, 3). +-define(SOCKET_OPT_SOCK_BINDTODEVICE, 3). -define(SOCKET_OPT_SOCK_BROADCAST, 4). %% -define(SOCKET_OPT_SOCK_BUSY_POLL, 5). -define(SOCKET_OPT_SOCK_DEBUG, 6). @@ -1959,6 +1959,8 @@ enc_setopt_value(otp, controlling_process, V, _, _, _) when is_pid(V) -> enc_setopt_value(otp = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); +enc_setopt_value(socket, bindtodevice, V, _D, _T, _P) when is_list(V) -> + V; enc_setopt_value(socket, broadcast, V, _D, _T, _P) when is_boolean(V) -> V; enc_setopt_value(socket, debug, V, _D, _T, _P) when is_integer(V) -> @@ -2253,10 +2255,10 @@ enc_sockopt_key(socket = _L, acceptconn = _Opt, get = _Dir, _D, _T, _P) -> enc_sockopt_key(socket = L, acceptfilter = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); %% Before linux 3.8, this socket option could be set. -%% Size of buffer for name: IFNAMSZ +%% Maximum size of buffer for name: IFNAMSZIZ %% So, we let the implementation decide. -enc_sockopt_key(socket = L, bindtodevide = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(socket = _L, bindtodevice = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SOCK_BINDTODEVICE; enc_sockopt_key(socket, broadcast = _Opt, _Dir, _D, dgram = _T, _P) -> ?SOCKET_OPT_SOCK_BROADCAST; enc_sockopt_key(socket = L, busy_poll = Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 8de18e84deaed4c9e6e7242ae2550fc6618dc44d Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 13 Jul 2018 18:38:53 +0200 Subject: [socket-nif] Add support for socket (level socket) option reuseport Added support for socket level socket option REUSEPORT. OTP-14831 --- erts/preloaded/src/socket.erl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index d4bf55511c..fbfd1903a1 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -517,7 +517,7 @@ %% -define(SOCKET_OPT_SOCK_RCVLOWAT, 21). %% -define(SOCKET_OPT_SOCK_RCVTIMEO, 22). -define(SOCKET_OPT_SOCK_REUSEADDR, 23). -%% -define(SOCKET_OPT_SOCK_REUSEPORT, 24). +-define(SOCKET_OPT_SOCK_REUSEPORT, 24). %% -define(SOCKET_OPT_SOCK_RXQ_OVFL, 25). %% -define(SOCKET_OPT_SOCK_SETFIB, 26). -define(SOCKET_OPT_SOCK_SNDBUF, 27). @@ -1984,6 +1984,8 @@ enc_setopt_value(socket, rcvbuf, V, _D, _T, _P) when is_integer(V) -> V; enc_setopt_value(socket, reuseaddr, V, _D, _T, _P) when is_boolean(V) -> V; +enc_setopt_value(socket, reuseport, V, _D, _T, _P) when is_boolean(V) -> + V; enc_setopt_value(socket, sndbuf, V, _D, _T, _P) when is_integer(V) -> V; enc_setopt_value(socket = L, Opt, V, _D, _T, _P) -> @@ -2301,11 +2303,11 @@ enc_sockopt_key(socket = L, rcvlowat = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(socket, rcvtimeo = Opt, _Dir, _D, _T, _P) -> not_supported(Opt); -enc_sockopt_key(socket, reuseaddr = _Opt, _Dir, _D, _T, _P) -> +enc_sockopt_key(socket = _L, reuseaddr = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SOCK_REUSEADDR; -enc_sockopt_key(socket = L, reuseport = Opt, _Dir, D, _T, _P) +enc_sockopt_key(socket = _L, reuseport = _Opt, _Dir, D, _T, _P) when ((D =:= inet) orelse (D =:= inet6)) -> - not_supported({L, Opt}); + ?SOCKET_OPT_SOCK_REUSEPORT; enc_sockopt_key(socket = L, rxq_ovfl = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(socket = L, setfib = Opt, set = _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 017565203f40860d24b80a54136a160aee460dbe Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 16 Jul 2018 18:21:48 +0200 Subject: [socket-nif] Add support for multiple acceptor processes Its now possible to have multiple (simultaneous) acceptor processes for the same listening socket. OTP-14831 --- erts/preloaded/src/socket.erl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index fbfd1903a1..96dc89bd9e 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -984,6 +984,7 @@ do_accept(LSockRef, SI, Timeout) -> Socket = #socket{info = SocketInfo, ref = SockRef}, {ok, Socket}; + {error, eagain} -> NewTimeout = next_timeout(TS, Timeout), receive @@ -997,7 +998,11 @@ do_accept(LSockRef, SI, Timeout) -> nif_cancel(LSockRef, accept, AccRef), flush_select_msgs(LSockRef, AccRef), {error, timeout} - end + end; + + {error, _} = ERROR -> + nif_cancel(LSockRef, accept, AccRef), % Just to be on the safe side... + ERROR end. -- cgit v1.2.3 From 180fd01ba04ff24e5b9cb8ab0098342f1d443b17 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 18 Jul 2018 09:30:34 +0200 Subject: [socket-nif] Preliminary (tcp) socket option updates Added default for code un-implemented TCP socket options. OTP-14831 --- erts/preloaded/src/socket.erl | 155 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 152 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 96dc89bd9e..64e0e6ce5b 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -291,7 +291,7 @@ ttl | unblock_source. -type ipv6_socket_option() :: - addform | + addrform | add_membership | authhdr | auth_level | @@ -327,8 +327,17 @@ -type tcp_socket_option() :: congestion | cork | + info | + keepidle | + keepintvl | + keepcnt | maxseg | - nodelay. + md5sig | + nodelay | + noopt | + nopush | + syncnt | + user_timeout. -type udp_socket_option() :: cork. @@ -2412,13 +2421,77 @@ enc_sockopt_key(ip = L, UnknownOpt, _Dir, _D, _T, _P) -> unknown({L, UnknownOpt}); %% IPv6 socket options +enc_sockopt_key(ipv6 = L, addrform = Opt, set = _Dir, _D, _T, _P) -> + not_supported({L, Opt}); enc_sockopt_key(ipv6, add_membership = _Opt, set = _Dir, _D, _T, _P) -> ?SOCKET_OPT_IPV6_ADD_MEMBERSHIP; +enc_sockopt_key(ipv6 = L, authhdr = Opt, set = _Dir, _D, T, _P) + when ((T =:= dgram) orelse (T =:= raw)) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, auth_level = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, checksum = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); enc_sockopt_key(ipv6, drop_membership = _Opt, set = _Dir, _D, _T, _P) -> ?SOCKET_OPT_IPV6_DROP_MEMBERSHIP; -enc_sockopt_key(ipv6, hoplimit = _Opt, _Dir, _D, T, _P) +enc_sockopt_key(ipv6 = L, dstopts = Opt, set = _Dir, _D, T, _P) + when (T =:= dgram) orelse (T =:= raw) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, esp_trans_level = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, esp_network_level = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, flowinfo = Opt, set = _Dir, _D, T, _P) + when (T =:= dgram) orelse (T =:= raw) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6, hoplimit = _Opt, _Dir, set = _D, T, _P) when (T =:= dgram) orelse (T =:= raw) -> ?SOCKET_OPT_IPV6_HOPLIMIT; +enc_sockopt_key(ipv6 = L, hopopts = Opt, set = _Dir, _D, T, _P) + when ((T =:= dgram) orelse (T =:= raw)) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, ipcomp_level = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, join_group = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, leave_group = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, mtu = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, mtu_discover = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, multicast_hops = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, multicast_if = Opt, _Dir, _D, T, _P) + when (T =:= dgram) orelse (T =:= raw) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, multicast_loop = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, portrange = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, pktinfo = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, pktoptions = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, recverr = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, recvpktinfo = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, recvtclass = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, router_alert = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, rthdr = Opt, set = _Dir, _D, T, _P) + when ((T =:= dgram) orelse (T =:= raw)) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, tclass = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, unicast_hops = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, use_min_mtu = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(ipv6 = L, v6only = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); enc_sockopt_key(ipv6 = L, UnknownOpt, _Dir, _D, _T, _P) -> unknown({L, UnknownOpt}); @@ -2429,10 +2502,26 @@ enc_sockopt_key(tcp, congestion = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_TCP_CONGESTION; enc_sockopt_key(tcp = L, cork = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); +enc_sockopt_key(tcp = L, keepidle = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(tcp = L, keepintvl = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(tcp = L, keepcnt = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); enc_sockopt_key(tcp, maxseg = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_TCP_MAXSEG; +enc_sockopt_key(tcp = L, md5sig = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); enc_sockopt_key(tcp, nodelay = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_TCP_NODELAY; +enc_sockopt_key(tcp = L, noopt = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(tcp = L, nopush = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(tcp = L, syncnt = Opt, _Dir, _D, _T, _P) -> % Only set? 1..255 + not_supported({L, Opt}); +enc_sockopt_key(tcp = L, user_timeout = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); enc_sockopt_key(tcp = L, UnknownOpt, _Dir, _D, _T, _P) -> unknown({L, UnknownOpt}); @@ -2443,10 +2532,70 @@ enc_sockopt_key(udp = L, UnknownOpt, _Dir, _D, _T, _P) -> unknown({L, UnknownOpt}); %% SCTP socket options +enc_sockopt_key(sctp = L, adaption_layer = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, associnfo = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, auth_active_key = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, auth_asconf = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, auth_chunk = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, auth_key = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, auth_delete_key = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); enc_sockopt_key(sctp, autoclose = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SCTP_AUTOCLOSE; +enc_sockopt_key(sctp = L, context = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, default_send_params = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, delayed_ack_time = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, disable_fragments = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, hmac_ident = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, events = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, explicit_eor = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, fragment_interleave = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, get_peer_addr_info = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, initmsg = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, i_want_mapped_v4_addr = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, local_auth_chunks = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, maxseg = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, maxburst = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); enc_sockopt_key(sctp, nodelay = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SCTP_NODELAY; +enc_sockopt_key(sctp = L, partial_delivery_point = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, peer_addr_params = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, peer_auth_chunks = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, primary_addr = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, reset_streams = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, rtoinfo = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, set_peer_primary_addr = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, status = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); +enc_sockopt_key(sctp = L, use_exp_recvinfo = Opt, _Dir, _D, _T, _P) -> + not_supported({L, Opt}); enc_sockopt_key(sctp = L, UnknownOpt, _Dir, _D, _T, _P) -> unknown({L, UnknownOpt}); -- cgit v1.2.3 From 8d759e6f00b1cf834fe21654de3f53df706e8c0f Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 18 Jul 2018 10:23:45 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option v6only Added support for the IPv6 option V6ONLY. OTP-14831 --- erts/preloaded/src/socket.erl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 64e0e6ce5b..3e31326759 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -602,7 +602,7 @@ %% -define(SOCKET_OPT_IPV6_TCLASS, 30). %% -define(SOCKET_OPT_IPV6_UNICAST_HOPS, 31). %% -define(SOCKET_OPT_IPV6_USE_MIN_MTU, 32). -%% -define(SOCKET_OPT_IPV6_V6ONLY, 33). +-define(SOCKET_OPT_IPV6_V6ONLY, 33). -define(SOCKET_OPT_TCP_CONGESTION, 1). -define(SOCKET_OPT_TCP_CORK, 2). @@ -2105,6 +2105,8 @@ enc_setopt_value(ipv6, drop_membership, #{multiaddr := MA, enc_setopt_value(ipv6, hoplimit, V, _D, T, _P) when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) -> V; +enc_setopt_value(ipv6, v6only, V, _D, _T, _P) when is_boolean(V) -> + V; enc_setopt_value(ipv6 = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); @@ -2490,8 +2492,8 @@ enc_sockopt_key(ipv6 = L, unicast_hops = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = L, use_min_mtu = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(ipv6 = L, v6only = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ipv6 = _L, v6only = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IPV6_V6ONLY; enc_sockopt_key(ipv6 = L, UnknownOpt, _Dir, _D, _T, _P) -> unknown({L, UnknownOpt}); -- cgit v1.2.3 From ebd626e7b4259bdfb4ddb34ce2d298d0feb0a1c8 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 18 Jul 2018 11:33:50 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option mtu Added support for the VPv6 socket option MTU. OTP-14831. --- erts/preloaded/src/socket.erl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 3e31326759..41df672ef5 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -586,7 +586,7 @@ %% -define(SOCKET_OPT_IPV6_IPCOMP_LEVEL, 14). %% -define(SOCKET_OPT_IPV6_JOIN_GROUP, 15). %% -define(SOCKET_OPT_IPV6_LEAVE_GROUP, 16). -%% -define(SOCKET_OPT_IPV6_MTU, 17). +-define(SOCKET_OPT_IPV6_MTU, 17). %% -define(SOCKET_OPT_IPV6_MTU_DISCOVER, 18). %% -define(SOCKET_OPT_IPV6_MULTICAST_HOPS, 19). %% -define(SOCKET_OPT_IPV6_MULTICAST_IF, 20). @@ -2105,6 +2105,8 @@ enc_setopt_value(ipv6, drop_membership, #{multiaddr := MA, enc_setopt_value(ipv6, hoplimit, V, _D, T, _P) when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) -> V; +enc_setopt_value(ipv6, mtu, V, _D, _T, _P) when is_integer(V) -> + V; enc_setopt_value(ipv6, v6only, V, _D, _T, _P) when is_boolean(V) -> V; enc_setopt_value(ipv6 = L, Opt, V, _D, _T, _P) -> @@ -2458,8 +2460,8 @@ enc_sockopt_key(ipv6 = L, join_group = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = L, leave_group = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(ipv6 = L, mtu = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ipv6 = _L, mtu = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IPV6_MTU; enc_sockopt_key(ipv6 = L, mtu_discover = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = L, multicast_hops = Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 1a3aca0a849af0bae994c9cf89de0dcfe7b310c2 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 18 Jul 2018 12:05:00 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option mtu_discover Added support for the VPv6 socket option MTU_DISCOVER. OTP-14831. --- erts/preloaded/src/socket.erl | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 41df672ef5..ead058c607 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -93,6 +93,7 @@ ip_mreq_source/0, ip_pmtudisc/0, ipv6_mreq/0, + ipv6_pmtudisc/0, msg_hdr/0 @@ -164,6 +165,8 @@ -type ipv6_mreq() :: #{multiaddr := ip6_address(), interface := non_neg_integer()}. +-type ipv6_pmtudisc() :: ip_pmtudisc(). + -type sockaddr_un() :: #{family := local, path := binary() | string()}. -type sockaddr_in4() :: #{family := inet, @@ -587,7 +590,7 @@ %% -define(SOCKET_OPT_IPV6_JOIN_GROUP, 15). %% -define(SOCKET_OPT_IPV6_LEAVE_GROUP, 16). -define(SOCKET_OPT_IPV6_MTU, 17). -%% -define(SOCKET_OPT_IPV6_MTU_DISCOVER, 18). +-define(SOCKET_OPT_IPV6_MTU_DISCOVER, 18). %% -define(SOCKET_OPT_IPV6_MULTICAST_HOPS, 19). %% -define(SOCKET_OPT_IPV6_MULTICAST_IF, 20). %% -define(SOCKET_OPT_IPV6_MULTICAST_LOOP, 21). @@ -2107,6 +2110,13 @@ enc_setopt_value(ipv6, hoplimit, V, _D, T, _P) V; enc_setopt_value(ipv6, mtu, V, _D, _T, _P) when is_integer(V) -> V; +enc_setopt_value(ipv6, mtu_discover, V, _D, _T, _P) + when (V =:= want) orelse + (V =:= dont) orelse + (V =:= do) orelse + (V =:= probe) orelse + is_integer(V) -> + V; enc_setopt_value(ipv6, v6only, V, _D, _T, _P) when is_boolean(V) -> V; enc_setopt_value(ipv6 = L, Opt, V, _D, _T, _P) -> @@ -2462,8 +2472,8 @@ enc_sockopt_key(ipv6 = L, leave_group = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = _L, mtu = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IPV6_MTU; -enc_sockopt_key(ipv6 = L, mtu_discover = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ipv6 = _L, mtu_discover = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IPV6_MTU_DISCOVER; enc_sockopt_key(ipv6 = L, multicast_hops = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = L, multicast_if = Opt, _Dir, _D, T, _P) -- cgit v1.2.3 From d28129b7098bce154264937862fcdafb21541433 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 19 Jul 2018 14:00:42 +0200 Subject: [socket-nif] Add support for socket (level sctp) option events Added support for the SCTP option EVENTS. OTP-14831 --- erts/preloaded/src/socket.erl | 117 +++++++++++++++++++++++++++++------------- 1 file changed, 80 insertions(+), 37 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index ead058c607..74b6bfd543 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -94,6 +94,7 @@ ip_pmtudisc/0, ipv6_mreq/0, ipv6_pmtudisc/0, + sctp_event_subscribe/0, msg_hdr/0 @@ -150,6 +151,14 @@ %% socket:setopt(Socket, ip, drop_membership, #{multiaddr => Addr, %% interface => any}). %% + +%% If the integer value is used its up to the caller to ensure its valid! +-type ip_tos_flag() :: lowdeley | + throughput | + reliability | + mincost | + integer(). + -type ip_mreq() :: #{multiaddr := ip4_address(), interface := any | ip4_address()}. %% -type ip_mreqn() :: #{multiaddr := ip4_address(), @@ -167,6 +176,17 @@ -type ipv6_pmtudisc() :: ip_pmtudisc(). +-type sctp_event_subscribe() :: #{data_in := boolean(), + association := boolean(), + address := boolean(), + send_failure := boolean(), + peer_error := boolean(), + shutdown := boolean(), + partial_delivery := boolean(), + adaptation_layer := boolean(), + authentication := boolean(), + sender_dry := boolean()}. + -type sockaddr_un() :: #{family := local, path := binary() | string()}. -type sockaddr_in4() :: #{family := inet, @@ -331,9 +351,9 @@ -type tcp_socket_option() :: congestion | cork | info | + keepcnt | keepidle | keepintvl | - keepcnt | maxseg | md5sig | nodelay | @@ -388,13 +408,6 @@ %% sctp_socket_option() | %% plain_socket_option(). -%% If the integer value is used its up to the caller to ensure its valid! --type ip_tos_flag() :: lowdeley | - throughput | - reliability | - mincost | - integer(). - -type socket_info() :: #{domain => domain(), type => type(), protocol => protocol()}. @@ -609,8 +622,17 @@ -define(SOCKET_OPT_TCP_CONGESTION, 1). -define(SOCKET_OPT_TCP_CORK, 2). --define(SOCKET_OPT_TCP_MAXSEG, 3). --define(SOCKET_OPT_TCP_NODELAY, 4). +%% -define(SOCKET_OPT_TCP_INFO, 3). +%% -define(SOCKET_OPT_TCP_KEEPCNT, 4). +%% -define(SOCKET_OPT_TCP_KEEPIDLE, 5). +%% -define(SOCKET_OPT_TCP_KEEPINTVL, 6). +-define(SOCKET_OPT_TCP_MAXSEG, 7). +%% -define(SOCKET_OPT_TCP_MD5SIG, 8). +-define(SOCKET_OPT_TCP_NODELAY, 9). +%% -define(SOCKET_OPT_TCP_NOOPT, 10). +%% -define(SOCKET_OPT_TCP_NOPUSH, 11). +%% -define(SOCKET_OPT_TCP_SYNCNT, 12). +%% -define(SOCKET_OPT_TCP_USER_TIMEOUT, 13). -define(SOCKET_OPT_UDP_CORK, 1). @@ -627,24 +649,25 @@ %% -define(SOCKET_OPT_SCTP_DELAYED_ACK_TIME, 11). %% -define(SOCKET_OPT_SCTP_DISABLE_FRAGMENTS, 12). %% -define(SOCKET_OPT_SCTP_HMAC_IDENT, 13). -%% -define(SOCKET_OPT_SCTP_EXPLICIT_EOR, 14). -%% -define(SOCKET_OPT_SCTP_FRAGMENT_INTERLEAVE, 15). -%% -define(SOCKET_OPT_SCTP_GET_PEER_ADDR_INFO, 16). -%% -define(SOCKET_OPT_SCTP_INITMSG, 17). -%% -define(SOCKET_OPT_SCTP_I_WANT_MAPPED_V4_ADDR, 18). -%% -define(SOCKET_OPT_SCTP_LOCAL_AUTH_CHUNKS, 19). -%% -define(SOCKET_OPT_SCTP_MAXSEG, 20). -%% -define(SOCKET_OPT_SCTP_MAXBURST, 21). --define(SOCKET_OPT_SCTP_NODELAY, 22). -%% -define(SOCKET_OPT_SCTP_PARTIAL_DELIVERY_POINT, 23). -%% -define(SOCKET_OPT_SCTP_PEER_ADDR_PARAMS, 24). -%% -define(SOCKET_OPT_SCTP_PEER_AUTH_CHUNKS, 25). -%% -define(SOCKET_OPT_SCTP_PRIMARY_ADDR, 26). -%% -define(SOCKET_OPT_SCTP_RESET_STREAMS, 27). -%% -define(SOCKET_OPT_SCTP_RTOINFO, 28). -%% -define(SOCKET_OPT_SCTP_SET_PEER_PRIMARY_ADDR, 29). -%% -define(SOCKET_OPT_SCTP_STATUS, 30). -%% -define(SOCKET_OPT_SCTP_USE_EXT_RECVINFO, 31). +-define(SOCKET_OPT_SCTP_EVENTS, 14). +%% -define(SOCKET_OPT_SCTP_EXPLICIT_EOR, 15). +%% -define(SOCKET_OPT_SCTP_FRAGMENT_INTERLEAVE, 16). +%% -define(SOCKET_OPT_SCTP_GET_PEER_ADDR_INFO, 17). +%% -define(SOCKET_OPT_SCTP_INITMSG, 18). +%% -define(SOCKET_OPT_SCTP_I_WANT_MAPPED_V4_ADDR, 19). +%% -define(SOCKET_OPT_SCTP_LOCAL_AUTH_CHUNKS, 20). +%% -define(SOCKET_OPT_SCTP_MAXSEG, 21). +%% -define(SOCKET_OPT_SCTP_MAXBURST, 22). +-define(SOCKET_OPT_SCTP_NODELAY, 23). +%% -define(SOCKET_OPT_SCTP_PARTIAL_DELIVERY_POINT, 24). +%% -define(SOCKET_OPT_SCTP_PEER_ADDR_PARAMS, 25). +%% -define(SOCKET_OPT_SCTP_PEER_AUTH_CHUNKS, 26). +%% -define(SOCKET_OPT_SCTP_PRIMARY_ADDR, 27). +%% -define(SOCKET_OPT_SCTP_RESET_STREAMS, 28). +%% -define(SOCKET_OPT_SCTP_RTOINFO, 29). +%% -define(SOCKET_OPT_SCTP_SET_PEER_PRIMARY_ADDR, 30). +%% -define(SOCKET_OPT_SCTP_STATUS, 31). +%% -define(SOCKET_OPT_SCTP_USE_EXT_RECVINFO, 32). -define(SOCKET_SHUTDOWN_HOW_READ, 0). -define(SOCKET_SHUTDOWN_HOW_WRITE, 1). @@ -1736,6 +1759,7 @@ setopt(#socket{info = Info, ref = SockRef}, Level, Key, Value) -> + %% =========================================================================== %% %% getopt - retrieve individual properties of a socket @@ -1942,6 +1966,8 @@ enc_setopt_level(tcp) -> {true, ?SOCKET_OPT_LEVEL_TCP}; enc_setopt_level(udp) -> {true, ?SOCKET_OPT_LEVEL_UDP}; +enc_setopt_level(sctp) -> + {true, ?SOCKET_OPT_LEVEL_SCTP}; %% Any option that is of an plain level must be provided as a binary %% already fully encoded! enc_setopt_level(L) when is_integer(L) -> @@ -2141,20 +2167,38 @@ enc_setopt_value(tcp = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); enc_setopt_value(udp, cork, V, _D, T, P) - when is_boolean(V) andalso - (T =:= dgram) andalso - (P =:= udp) -> + when is_boolean(V) andalso (T =:= dgram) andalso (P =:= udp) -> V; enc_setopt_value(udp = L, Opt, _V, _D, _T, _P) -> not_supported({L, Opt}); enc_setopt_value(sctp, autoclose, V, _D, _T, P) - when is_integer(V) andalso + when is_integer(V) andalso (P =:= sctp) -> + V; +enc_setopt_value(sctp, events, #{data_in := DataIn, + association := Assoc, + address := Addr, + send_failure := SndFailure, + peer_error := PeerError, + shutdown := Shutdown, + partial_delivery := PartialDelivery, + adaptation_layer := AdaptLayer, + authentication := Auth, + sender_dry := SndDry} = V, _D, _T, P) + when is_boolean(DataIn) andalso + is_boolean(Assoc) andalso + is_boolean(Addr) andalso + is_boolean(SndFailure) andalso + is_boolean(PeerError) andalso + is_boolean(Shutdown) andalso + is_boolean(PartialDelivery) andalso + is_boolean(AdaptLayer) andalso + is_boolean(Auth) andalso + is_boolean(SndDry) andalso (P =:= sctp) -> V; enc_setopt_value(sctp, nodelay, V, _D, _T, P) - when is_boolean(V) andalso - (P =:= sctp) -> + when is_boolean(V) andalso (P =:= sctp) -> V; enc_setopt_value(L, Opt, V, _, _, _) @@ -2572,8 +2616,8 @@ enc_sockopt_key(sctp = L, disable_fragments = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp = L, hmac_ident = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(sctp = L, events = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(sctp = _L, events = _Opt, set = _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SCTP_EVENTS; enc_sockopt_key(sctp = L, explicit_eor = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp = L, fragment_interleave = Opt, _Dir, _D, _T, _P) -> @@ -2727,7 +2771,6 @@ tdiff(T1, T2) -> - %% p(F) -> %% p(F, []). -- cgit v1.2.3 From f0a2e68a31ac585780ad05f777f1b7551770420e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 19 Jul 2018 14:36:34 +0200 Subject: [socket-nif] Add support for socket (level sctp) option disable_fragments Added support for the SCTP option DISABLE_FRAGMENTS. OTP-14831 --- erts/preloaded/src/socket.erl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 74b6bfd543..e3fb417a35 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -647,7 +647,7 @@ %% -define(SOCKET_OPT_SCTP_CONTEXT, 9). %% -define(SOCKET_OPT_SCTP_DEFAULT_SEND_PARAMS, 10). %% -define(SOCKET_OPT_SCTP_DELAYED_ACK_TIME, 11). -%% -define(SOCKET_OPT_SCTP_DISABLE_FRAGMENTS, 12). +-define(SOCKET_OPT_SCTP_DISABLE_FRAGMENTS, 12). %% -define(SOCKET_OPT_SCTP_HMAC_IDENT, 13). -define(SOCKET_OPT_SCTP_EVENTS, 14). %% -define(SOCKET_OPT_SCTP_EXPLICIT_EOR, 15). @@ -2175,6 +2175,9 @@ enc_setopt_value(udp = L, Opt, _V, _D, _T, _P) -> enc_setopt_value(sctp, autoclose, V, _D, _T, P) when is_integer(V) andalso (P =:= sctp) -> V; +enc_setopt_value(sctp, disable_fragments, V, _D, _T, P) + when is_boolean(V) andalso (P =:= sctp) -> + V; enc_setopt_value(sctp, events, #{data_in := DataIn, association := Assoc, address := Addr, @@ -2612,8 +2615,8 @@ enc_sockopt_key(sctp = L, default_send_params = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp = L, delayed_ack_time = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(sctp = L, disable_fragments = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(sctp = _L, disable_fragments = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SCTP_DISABLE_FRAGMENTS; enc_sockopt_key(sctp = L, hmac_ident = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp = _L, events = _Opt, set = _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 7a5b320b5bb5ec45b21839005e8538172908fb57 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 19 Jul 2018 16:39:13 +0200 Subject: [socket-nif] Add (partial) support for socket (level sctp) option associnfo Added support for the SCTP option ASSOCINFO. This option is a bit tricky. As the underlying structure (sctp_assocparams) contains the assoc_id, it begs the question what happens if this option is fetched for: * The own assoc (which means that we might have the assoc id in the descriptor and can initiate that part of the struct accordningly). * Another assoc: From assoc A asks for info with assoc_id set to that of assoc B. * The "owning" endpoint. * Another endpoint (an endpoint to which the assoc does not belong). So, if the user calls socket:[getopt|setopt] for an association socket, shall we require that the assoc_id field is set to -1? Or not set at all and therefor filled in automatically by the nif-code? And, if the user calls socket:[getopt|setopt] for an endpoint socket, shall we require that the assoc_id field is set to a valid id? Or shall it not be allowed? Questions, questions... OTP-14831 --- erts/preloaded/src/socket.erl | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index e3fb417a35..feb2e25312 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -95,6 +95,7 @@ ipv6_mreq/0, ipv6_pmtudisc/0, sctp_event_subscribe/0, + sctp_assocparams/0, msg_hdr/0 @@ -187,6 +188,13 @@ authentication := boolean(), sender_dry := boolean()}. +-type sctp_assocparams() :: #{assoc_id := integer(), + max_rxt := non_neg_integer(), + num_peer_dests := non_neg_integer(), + peer_rwnd := non_neg_integer(), + local_rwnd := non_neg_integer(), + cookie_life := non_neg_integer()}. + -type sockaddr_un() :: #{family := local, path := binary() | string()}. -type sockaddr_in4() :: #{family := inet, @@ -637,7 +645,7 @@ -define(SOCKET_OPT_UDP_CORK, 1). %% -define(SOCKET_OPT_SCTP_ADAPTION_LAYER, 1). -%% -define(SOCKET_OPT_SCTP_ASSOCINFO, 2). +-define(SOCKET_OPT_SCTP_ASSOCINFO, 2). %% -define(SOCKET_OPT_SCTP_AUTH_ACTIVE_KEY, 3). %% -define(SOCKET_OPT_SCTP_AUTH_ASCONF, 4). %% -define(SOCKET_OPT_SCTP_AUTH_CHUNK, 5). @@ -2172,8 +2180,23 @@ enc_setopt_value(udp, cork, V, _D, T, P) enc_setopt_value(udp = L, Opt, _V, _D, _T, _P) -> not_supported({L, Opt}); +enc_setopt_value(sctp, associnfo, #{assoc_id := AssocId, + asocmaxrxt := MaxRxt, + num_peer_dests := NumPeerDests, + peer_rwnd := PeerRWND, + local_rwnd := LocalRWND, + cookie_life := CLife} = V, + _D, _T, P) + when is_integer(AssocId) andalso + is_integer(MaxRxt) andalso (MaxRxt >= 0) andalso + is_integer(NumPeerDests) andalso (NumPeerDests >= 0) andalso + is_integer(PeerRWND) andalso (PeerRWND >= 0) andalso + is_integer(LocalRWND) andalso (LocalRWND >= 0) andalso + is_integer(CLife) andalso (CLife >= 0) andalso + (P =:= sctp) -> + V; enc_setopt_value(sctp, autoclose, V, _D, _T, P) - when is_integer(V) andalso (P =:= sctp) -> + when is_integer(V) andalso (V >= 0) andalso (P =:= sctp) -> V; enc_setopt_value(sctp, disable_fragments, V, _D, _T, P) when is_boolean(V) andalso (P =:= sctp) -> @@ -2595,8 +2618,8 @@ enc_sockopt_key(udp = L, UnknownOpt, _Dir, _D, _T, _P) -> %% SCTP socket options enc_sockopt_key(sctp = L, adaption_layer = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(sctp = L, associnfo = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(sctp = _L, associnfo = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SCTP_ASSOCINFO; enc_sockopt_key(sctp = L, auth_active_key = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp = L, auth_asconf = Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 6bb60f1fecef368991806e25a0022c63b8650f39 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 19 Jul 2018 17:34:15 +0200 Subject: [socket-nif] Add (partial) support for socket (level sctp) option rtoinfo Added support for the SCTP option RTOINFO. We have the same questions for this option as for ASSOCINFO. Maybe the assoc field shall be made 'out' only (that is, it will be ignored for setopt). The assoc id used will be that which is stored in the descriptor (how do we get it to begin with?). Questions, questions... OTP-14831 --- erts/preloaded/src/socket.erl | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index feb2e25312..0bc3ed8ec9 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -96,6 +96,7 @@ ipv6_pmtudisc/0, sctp_event_subscribe/0, sctp_assocparams/0, + sctp_rtoinfo/0, msg_hdr/0 @@ -195,6 +196,11 @@ local_rwnd := non_neg_integer(), cookie_life := non_neg_integer()}. +-type sctp_rtoinfo() :: #{assoc_id := integer(), + initial := non_neg_integer(), + max := non_neg_integer(), + min := non_neg_integer()}. + -type sockaddr_un() :: #{family := local, path := binary() | string()}. -type sockaddr_in4() :: #{family := inet, @@ -672,7 +678,7 @@ %% -define(SOCKET_OPT_SCTP_PEER_AUTH_CHUNKS, 26). %% -define(SOCKET_OPT_SCTP_PRIMARY_ADDR, 27). %% -define(SOCKET_OPT_SCTP_RESET_STREAMS, 28). -%% -define(SOCKET_OPT_SCTP_RTOINFO, 29). +-define(SOCKET_OPT_SCTP_RTOINFO, 29). %% -define(SOCKET_OPT_SCTP_SET_PEER_PRIMARY_ADDR, 30). %% -define(SOCKET_OPT_SCTP_STATUS, 31). %% -define(SOCKET_OPT_SCTP_USE_EXT_RECVINFO, 32). @@ -2226,7 +2232,21 @@ enc_setopt_value(sctp, events, #{data_in := DataIn, enc_setopt_value(sctp, nodelay, V, _D, _T, P) when is_boolean(V) andalso (P =:= sctp) -> V; +enc_setopt_value(sctp, rtoinfo, #{assoc_id := AssocId, + initial := Init, + max := Max, + min := Min} = V, + _D, _T, P) + when is_integer(AssocId) andalso + is_integer(Init) andalso (Init >= 0) andalso + is_integer(Max) andalso (Max >= 0) andalso + is_integer(Min) andalso (Min >= 0) andalso + (P =:= sctp) -> + V; +enc_setopt_value(sctp = L, Opt, V, _D, _T, _P) -> + not_supported({L, Opt, V}); +%% Is this correct? What about getopt? enc_setopt_value(L, Opt, V, _, _, _) when is_integer(L) andalso is_integer(Opt) andalso is_binary(V) -> V. @@ -2672,8 +2692,8 @@ enc_sockopt_key(sctp = L, primary_addr = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp = L, reset_streams = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(sctp = L, rtoinfo = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(sctp = _L, rtoinfo = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SCTP_RTOINFO; enc_sockopt_key(sctp = L, set_peer_primary_addr = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp = L, status = Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 08ce39bbc2cd3006475c87807042c2d08a68736f Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 19 Jul 2018 17:54:14 +0200 Subject: [socket-nif] Add support for socket (level sctp) option maxseg Added support for the SCTP option MAXSEG. OTP-14831 --- erts/preloaded/src/socket.erl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 0bc3ed8ec9..1c24c75dac 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -670,7 +670,7 @@ %% -define(SOCKET_OPT_SCTP_INITMSG, 18). %% -define(SOCKET_OPT_SCTP_I_WANT_MAPPED_V4_ADDR, 19). %% -define(SOCKET_OPT_SCTP_LOCAL_AUTH_CHUNKS, 20). -%% -define(SOCKET_OPT_SCTP_MAXSEG, 21). +-define(SOCKET_OPT_SCTP_MAXSEG, 21). %% -define(SOCKET_OPT_SCTP_MAXBURST, 22). -define(SOCKET_OPT_SCTP_NODELAY, 23). %% -define(SOCKET_OPT_SCTP_PARTIAL_DELIVERY_POINT, 24). @@ -2229,6 +2229,9 @@ enc_setopt_value(sctp, events, #{data_in := DataIn, is_boolean(SndDry) andalso (P =:= sctp) -> V; +enc_setopt_value(sctp, maxseg, V, _D, _T, P) + when is_integer(V) andalso (V >= 0) andalso (P =:= sctp) -> + V; enc_setopt_value(sctp, nodelay, V, _D, _T, P) when is_boolean(V) andalso (P =:= sctp) -> V; @@ -2676,8 +2679,8 @@ enc_sockopt_key(sctp = L, i_want_mapped_v4_addr = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp = L, local_auth_chunks = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(sctp = L, maxseg = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(sctp = _L, maxseg = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SCTP_MAXSEG; enc_sockopt_key(sctp = L, maxburst = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp, nodelay = _Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 8b96072a94d590ad545fa888911e5d613b32281e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 19 Jul 2018 18:22:05 +0200 Subject: [socket-nif] Add preliminary bind/3 for sctp Added a preliminary bind/3 that shall map to sctp_bindx. We need similar functions for sctp_connectx, and maybe a bunch of other specific sctp function(s). --- erts/preloaded/src/socket.erl | 73 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 1c24c75dac..cadbf1131c 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -31,7 +31,7 @@ -export([ open/2, open/3, open/4, - bind/2, + bind/2, bind/3, connect/2, connect/3, listen/1, listen/2, accept/1, accept/2, @@ -899,6 +899,70 @@ bind(#socket{ref = SockRef} = _Socket, Addr) when is_map(Addr) -> +%% =========================================================================== +%% +%% bind - Add or remove a bind addresses on a socket +%% +%% Calling this function is only valid if the socket is: +%% type = seqpacket +%% protocol = sctp +%% +%% If the domain is inet, then all addresses *must* be IPv4. +%% If the domain is inet6, the addresses can be aither IPv4 or IPv6. +%% + +-spec bind(Socket, Addrs, Action) -> ok | {error, Reason} when + Socket :: socket(), + Addrs :: [sockaddr()], + Action :: add | remove, + Reason :: term(). + +bind(#socket{ref = SockRef, + info = #{domain := Domain, + type := seqpacket, + protocol := sctp}} = _Socket, Addrs, Action) + when is_list(Addrs) andalso ((Action =:= add) orelse (Action =:= remove)) -> + try + begin + validate_addrs(Domain, Addrs), + nif_bind(SockRef, Addrs, Action) + end + catch + throw:ERROR -> + ERROR + end. + + +validate_addrs(inet = _Domain, Addrs) -> + validate_inet_addrs(Addrs); +validate_addrs(inet6 = _Domain, Addrs) -> + validate_inet6_addrs(Addrs). + +validate_inet_addrs(Addrs) -> + Validator = fun(#{family := inet, + addrs := Addr}) when is_tuple(Addr) andalso + (size(Addr) =:= 4) -> + ok; + (X) -> + throw({error, {invalid_address, X}}) + end, + lists:foreach(Validator, Addrs). + +validate_inet6_addrs(Addrs) -> + Validator = fun(#{family := inet, + addrs := Addr}) when is_tuple(Addr) andalso + (size(Addr) =:= 4) -> + ok; + (#{family := inet6, + addrs := Addr}) when is_tuple(Addr) andalso + (size(Addr) =:= 8) -> + ok; + (X) -> + throw({error, {invalid_address, X}}) + end, + lists:foreach(Validator, Addrs). + + %% =========================================================================== %% %% connect - initiate a connection on a socket @@ -2699,8 +2763,8 @@ enc_sockopt_key(sctp = _L, rtoinfo = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SCTP_RTOINFO; enc_sockopt_key(sctp = L, set_peer_primary_addr = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(sctp = L, status = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(sctp = L, status = Opt, get = _Dir, _D, _T, _P) -> + not_supported({L, Opt}); % ?SOCKET_OPT_SCTP_RTOINFO; enc_sockopt_key(sctp = L, use_exp_recvinfo = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp = L, UnknownOpt, _Dir, _D, _T, _P) -> @@ -2869,6 +2933,9 @@ nif_open(_Domain, _Type, _Protocol, _Extra) -> nif_bind(_SRef, _SockAddr) -> erlang:error(badarg). +nif_bind(_SRef, _SockAddrs, _Action) -> + erlang:error(badarg). + nif_connect(_SRef, _SockAddr) -> erlang:error(badarg). -- cgit v1.2.3 From bd36af21717b138c91724128e592b3fc587bb07a Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 20 Jul 2018 10:10:28 +0200 Subject: [socket-nif] Add support for socket (level sctp) option initmsg Added support for the SCTP option INITMSG. OTP-14831 --- erts/preloaded/src/socket.erl | 57 ++++++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 14 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index cadbf1131c..03c87a6df5 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -96,16 +96,30 @@ ipv6_pmtudisc/0, sctp_event_subscribe/0, sctp_assocparams/0, + sctp_initmsg/0, sctp_rtoinfo/0, - msg_hdr/0 + msg_hdr/0, + + uint8/0, + uint16/0, + uint20/0, + uint32/0 ]). + +-type uint8() :: 0..16#FF. +-type uint16() :: 0..16#FFFF. +-type uint20() :: 0..16#FFFFF. +-type uint32() :: 0..16#FFFFFFFF. + + %% We support only a subset of all domains. -type domain() :: local | inet | inet6. %% We support only a subset of all types. +%% RDM - Reliably Delivered Messages -type type() :: stream | dgram | raw | rdm | seqpacket. %% We support only a subset of all protocols: @@ -117,8 +131,6 @@ -type ip4_address() :: {0..255, 0..255, 0..255, 0..255}. --type uint20() :: 0..16#FFFFF. --type uint32() :: 0..16#FFFFFFFF. -type in6_flow_info() :: uint20(). -type in6_scope_id() :: uint32(). @@ -190,16 +202,22 @@ sender_dry := boolean()}. -type sctp_assocparams() :: #{assoc_id := integer(), - max_rxt := non_neg_integer(), - num_peer_dests := non_neg_integer(), - peer_rwnd := non_neg_integer(), - local_rwnd := non_neg_integer(), - cookie_life := non_neg_integer()}. + max_rxt := uint16(), + num_peer_dests := uint16(), + peer_rwnd := uint32(), + local_rwnd := uint32(), + cookie_life := uint32()}. + +-type sctp_initmsg() :: #{num_outstreams := uint16(), + max_instreams := uint16(), + max_attempts := uint16(), + max_init_timeo := uint16() + }. -type sctp_rtoinfo() :: #{assoc_id := integer(), - initial := non_neg_integer(), - max := non_neg_integer(), - min := non_neg_integer()}. + initial := uint32(), + max := uint32(), + min := uint32()}. -type sockaddr_un() :: #{family := local, path := binary() | string()}. @@ -667,7 +685,7 @@ %% -define(SOCKET_OPT_SCTP_EXPLICIT_EOR, 15). %% -define(SOCKET_OPT_SCTP_FRAGMENT_INTERLEAVE, 16). %% -define(SOCKET_OPT_SCTP_GET_PEER_ADDR_INFO, 17). -%% -define(SOCKET_OPT_SCTP_INITMSG, 18). +-define(SOCKET_OPT_SCTP_INITMSG, 18). %% -define(SOCKET_OPT_SCTP_I_WANT_MAPPED_V4_ADDR, 19). %% -define(SOCKET_OPT_SCTP_LOCAL_AUTH_CHUNKS, 20). -define(SOCKET_OPT_SCTP_MAXSEG, 21). @@ -2293,6 +2311,17 @@ enc_setopt_value(sctp, events, #{data_in := DataIn, is_boolean(SndDry) andalso (P =:= sctp) -> V; +enc_setopt_value(sctp, initmsg, #{num_outstreams := NumOut, + max_instreams := MaxIn, + max_attempts := MaxAttempts, + max_init_timeo := MaxInitTO} = V, + _D, _T, P) + when is_integer(NumOut) andalso (NumOut >= 0) andalso + is_integer(MaxIn) andalso (MaxIn >= 0) andalso + is_integer(MaxAttempts) andalso (MaxAttempts >= 0) andalso + is_integer(MaxInitTO) andalso (MaxInitTO >= 0) andalso + (P =:= sctp) -> + V; enc_setopt_value(sctp, maxseg, V, _D, _T, P) when is_integer(V) andalso (V >= 0) andalso (P =:= sctp) -> V; @@ -2737,8 +2766,8 @@ enc_sockopt_key(sctp = L, fragment_interleave = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp = L, get_peer_addr_info = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(sctp = L, initmsg = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(sctp = _L, initmsg = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SCTP_INITMSG; enc_sockopt_key(sctp = L, i_want_mapped_v4_addr = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(sctp = L, local_auth_chunks = Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 3f1d17f3031b71ca6ff1f8e051859ad55e55822b Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 20 Jul 2018 12:28:19 +0200 Subject: [socket-nif] Add support for socket (level socket) option(s) [rcv|snd]timeo Added support for socket level socket option RCVTIMEO and SNDTIMEO. These are both a little strange, at least on linux. See the man pages for more info. OTP-14831 --- erts/preloaded/src/socket.erl | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 03c87a6df5..15f9693490 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -88,6 +88,7 @@ udp_socket_option/0, sctp_socket_option/0, + timeval/0, ip_tos_flag/0, ip_mreq/0, ip_mreq_source/0, @@ -101,7 +102,7 @@ msg_hdr/0, - + uint8/0, uint16/0, uint20/0, @@ -154,6 +155,16 @@ %% %% +-type timeval() :: #{sec := integer(), + usec := integer()}. + +%% If the integer value is used its up to the caller to ensure its valid! +-type ip_tos_flag() :: lowdeley | + throughput | + reliability | + mincost | + integer(). + %% This type is used when requesting to become member of a multicast %% group with a call to setopt. Example: %% @@ -166,13 +177,6 @@ %% interface => any}). %% -%% If the integer value is used its up to the caller to ensure its valid! --type ip_tos_flag() :: lowdeley | - throughput | - reliability | - mincost | - integer(). - -type ip_mreq() :: #{multiaddr := ip4_address(), interface := any | ip4_address()}. %% -type ip_mreqn() :: #{multiaddr := ip4_address(), @@ -572,7 +576,7 @@ -define(SOCKET_OPT_SOCK_RCVBUF, 19). %% -define(SOCKET_OPT_SOCK_RCVBUFFORCE, 20). %% -define(SOCKET_OPT_SOCK_RCVLOWAT, 21). -%% -define(SOCKET_OPT_SOCK_RCVTIMEO, 22). +-define(SOCKET_OPT_SOCK_RCVTIMEO, 22). -define(SOCKET_OPT_SOCK_REUSEADDR, 23). -define(SOCKET_OPT_SOCK_REUSEPORT, 24). %% -define(SOCKET_OPT_SOCK_RXQ_OVFL, 25). @@ -580,7 +584,7 @@ -define(SOCKET_OPT_SOCK_SNDBUF, 27). %% -define(SOCKET_OPT_SOCK_SNDBUFFORCE, 28). %% -define(SOCKET_OPT_SOCK_SNDLOWAT, 29). -%% -define(SOCKET_OPT_SOCK_SNDTIMEO, 30). +-define(SOCKET_OPT_SOCK_SNDTIMEO, 30). %% -define(SOCKET_OPT_SOCK_TIMESTAMP, 31). -define(SOCKET_OPT_SOCK_TYPE, 32). @@ -2121,12 +2125,18 @@ enc_setopt_value(socket, priority, V, _D, _T, _P) when is_integer(V) -> V; enc_setopt_value(socket, rcvbuf, V, _D, _T, _P) when is_integer(V) -> V; +enc_setopt_value(socket, rcvtimeo, #{sec := Sec, usec := USec} = V, _D, _T, _P) + when is_integer(Sec) andalso is_integer(USec) -> + V; enc_setopt_value(socket, reuseaddr, V, _D, _T, _P) when is_boolean(V) -> V; enc_setopt_value(socket, reuseport, V, _D, _T, _P) when is_boolean(V) -> V; enc_setopt_value(socket, sndbuf, V, _D, _T, _P) when is_integer(V) -> V; +enc_setopt_value(socket, sndtimeo, #{sec := Sec, usec := USec} = V, _D, _T, _P) + when is_integer(Sec) andalso is_integer(USec) -> + V; enc_setopt_value(socket = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); @@ -2515,8 +2525,8 @@ enc_sockopt_key(socket = L, rcvbufforce = Opt, _Dir, _D, _T, _P) -> %% May not work on linux. enc_sockopt_key(socket = L, rcvlowat = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(socket, rcvtimeo = Opt, _Dir, _D, _T, _P) -> - not_supported(Opt); +enc_sockopt_key(socket = _L, rcvtimeo = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SOCK_RCVTIMEO; enc_sockopt_key(socket = _L, reuseaddr = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SOCK_REUSEADDR; enc_sockopt_key(socket = _L, reuseport = _Opt, _Dir, D, _T, _P) @@ -2526,15 +2536,15 @@ enc_sockopt_key(socket = L, rxq_ovfl = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(socket = L, setfib = Opt, set = _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(socket, sndbuf = _Opt, _Dir, _D, _T, _P) -> +enc_sockopt_key(socket = _L, sndbuf = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SOCK_SNDBUF; enc_sockopt_key(socket = L, sndbufforce = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); %% Not changeable on linux. enc_sockopt_key(socket = L, sndlowat = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(socket = L, sndtimeo = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(socket = _L, sndtimeo = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SOCK_SNDTIMEO; enc_sockopt_key(socket = L, timestamp = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(socket = _L, type = _Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 75498c0dd7682bae7787c4e2cd8d2680fa2b9c45 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 20 Jul 2018 13:00:22 +0200 Subject: [socket-nif] Add support for socket (level socket) option(s) [rcv|snd]lowat Added support for socket level socket option RCVLOWAT and SNDLOWAT. These are both a little strange, at least on Linux. See the man pages for more info. For instance, sndlowat cannot be set on Linux. OTP-14831 --- erts/preloaded/src/socket.erl | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 15f9693490..3ccba06f6b 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -575,7 +575,7 @@ -define(SOCKET_OPT_SOCK_PROTOCOL, 18). -define(SOCKET_OPT_SOCK_RCVBUF, 19). %% -define(SOCKET_OPT_SOCK_RCVBUFFORCE, 20). -%% -define(SOCKET_OPT_SOCK_RCVLOWAT, 21). +-define(SOCKET_OPT_SOCK_RCVLOWAT, 21). -define(SOCKET_OPT_SOCK_RCVTIMEO, 22). -define(SOCKET_OPT_SOCK_REUSEADDR, 23). -define(SOCKET_OPT_SOCK_REUSEPORT, 24). @@ -583,7 +583,7 @@ %% -define(SOCKET_OPT_SOCK_SETFIB, 26). -define(SOCKET_OPT_SOCK_SNDBUF, 27). %% -define(SOCKET_OPT_SOCK_SNDBUFFORCE, 28). -%% -define(SOCKET_OPT_SOCK_SNDLOWAT, 29). +-define(SOCKET_OPT_SOCK_SNDLOWAT, 29). -define(SOCKET_OPT_SOCK_SNDTIMEO, 30). %% -define(SOCKET_OPT_SOCK_TIMESTAMP, 31). -define(SOCKET_OPT_SOCK_TYPE, 32). @@ -2125,6 +2125,8 @@ enc_setopt_value(socket, priority, V, _D, _T, _P) when is_integer(V) -> V; enc_setopt_value(socket, rcvbuf, V, _D, _T, _P) when is_integer(V) -> V; +enc_setopt_value(socket, rcvlowat, V, _D, _T, _P) when is_integer(V) -> + V; enc_setopt_value(socket, rcvtimeo, #{sec := Sec, usec := USec} = V, _D, _T, _P) when is_integer(Sec) andalso is_integer(USec) -> V; @@ -2134,6 +2136,8 @@ enc_setopt_value(socket, reuseport, V, _D, _T, _P) when is_boolean(V) -> V; enc_setopt_value(socket, sndbuf, V, _D, _T, _P) when is_integer(V) -> V; +enc_setopt_value(socket, sndlowat, V, _D, _T, _P) when is_integer(V) -> + V; enc_setopt_value(socket, sndtimeo, #{sec := Sec, usec := USec} = V, _D, _T, _P) when is_integer(Sec) andalso is_integer(USec) -> V; @@ -2523,8 +2527,8 @@ enc_sockopt_key(socket, rcvbuf = _Opt, _Dir, _D, _T, _P) -> enc_sockopt_key(socket = L, rcvbufforce = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); %% May not work on linux. -enc_sockopt_key(socket = L, rcvlowat = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(socket = _L, rcvlowat = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SOCK_RCVLOWAT; enc_sockopt_key(socket = _L, rcvtimeo = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SOCK_RCVTIMEO; enc_sockopt_key(socket = _L, reuseaddr = _Opt, _Dir, _D, _T, _P) -> @@ -2541,8 +2545,8 @@ enc_sockopt_key(socket = _L, sndbuf = _Opt, _Dir, _D, _T, _P) -> enc_sockopt_key(socket = L, sndbufforce = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); %% Not changeable on linux. -enc_sockopt_key(socket = L, sndlowat = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(socket = _L, sndlowat = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SOCK_SNDLOWAT; enc_sockopt_key(socket = _L, sndtimeo = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SOCK_SNDTIMEO; enc_sockopt_key(socket = L, timestamp = Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 84f62ae80dd08874d0d5fbedc532605394e897c1 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 20 Jul 2018 14:01:14 +0200 Subject: [socket-nif] Add support for socket (level socket) option timestamp Added support for socket level socket option TIMESTAMP. OTP-14831 --- erts/preloaded/src/socket.erl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 3ccba06f6b..c231647f8c 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -556,7 +556,7 @@ -define(SOCKET_OPT_OTP_CTRL_PROC, 3). -define(SOCKET_OPT_SOCK_ACCEPTCONN, 1). -%% -define(SOCKET_OPT_SOCK_ACCEPTFILTER, 2). +%% -define(SOCKET_OPT_SOCK_ACCEPTFILTER, 2). % FreeBSD -define(SOCKET_OPT_SOCK_BINDTODEVICE, 3). -define(SOCKET_OPT_SOCK_BROADCAST, 4). %% -define(SOCKET_OPT_SOCK_BUSY_POLL, 5). @@ -580,12 +580,12 @@ -define(SOCKET_OPT_SOCK_REUSEADDR, 23). -define(SOCKET_OPT_SOCK_REUSEPORT, 24). %% -define(SOCKET_OPT_SOCK_RXQ_OVFL, 25). -%% -define(SOCKET_OPT_SOCK_SETFIB, 26). +%% -define(SOCKET_OPT_SOCK_SETFIB, 26). % FreeBSD -define(SOCKET_OPT_SOCK_SNDBUF, 27). %% -define(SOCKET_OPT_SOCK_SNDBUFFORCE, 28). -define(SOCKET_OPT_SOCK_SNDLOWAT, 29). -define(SOCKET_OPT_SOCK_SNDTIMEO, 30). -%% -define(SOCKET_OPT_SOCK_TIMESTAMP, 31). +-define(SOCKET_OPT_SOCK_TIMESTAMP, 31). -define(SOCKET_OPT_SOCK_TYPE, 32). -define(SOCKET_OPT_IP_ADD_MEMBERSHIP, 1). @@ -2141,6 +2141,8 @@ enc_setopt_value(socket, sndlowat, V, _D, _T, _P) when is_integer(V) -> enc_setopt_value(socket, sndtimeo, #{sec := Sec, usec := USec} = V, _D, _T, _P) when is_integer(Sec) andalso is_integer(USec) -> V; +enc_setopt_value(socket, timestamp, V, _D, _T, _P) when is_boolean(V) -> + V; enc_setopt_value(socket = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); @@ -2549,8 +2551,8 @@ enc_sockopt_key(socket = _L, sndlowat = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SOCK_SNDLOWAT; enc_sockopt_key(socket = _L, sndtimeo = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SOCK_SNDTIMEO; -enc_sockopt_key(socket = L, timestamp = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(socket = _L, timestamp = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_SOCK_TIMESTAMP; enc_sockopt_key(socket = _L, type = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SOCK_TYPE; enc_sockopt_key(socket = L, UnknownOpt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 1c26ae984a79224ce64b40dbc7239bf9721bb096 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 20 Jul 2018 15:14:06 +0200 Subject: [socket-nif] Add support for socket (level ip) option freebind Added support for ip level socket option FREEBIND. Note that there is an option available on FreeBSD called IP_BINDANY, which seems to have similar properties (FREEBIND is *not* available on FreeBSD). There are some restrictions for this option though (which is not mentioned in the Linux man page). OTP-14831 --- erts/preloaded/src/socket.erl | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index c231647f8c..329223c443 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -87,6 +87,7 @@ tcp_socket_option/0, udp_socket_option/0, sctp_socket_option/0, + raw_socket_option/0, timeval/0, ip_tos_flag/0, @@ -258,7 +259,7 @@ %% Int - Raw level, sent down and used "as is". -type sockopt_level() :: otp | socket | - ip | ipv6 | tcp | udp | sctp | + ip | ipv6 | tcp | udp | sctp | raw | non_neg_integer(). %% There are some options that are 'read-only'. @@ -434,6 +435,8 @@ status | use_ext_recvinfo. +-type raw_socket_option() :: filter. + %% -type plain_socket_option() :: integer(). %% -type sockopt() :: otp_socket_option() | %% socket_option() | @@ -442,6 +445,7 @@ %% tcp_socket_option() | %% udp_socket_option() | %% sctp_socket_option() | +%% raw_socket_option() | %% plain_socket_option(). -type socket_info() :: #{domain => domain(), @@ -594,7 +598,7 @@ %% -define(SOCKET_OPT_IP_DONTFRAG, 4). -define(SOCKET_OPT_IP_DROP_MEMBERSHIP, 5). -define(SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP, 6). -%% -define(SOCKET_OPT_IP_FREEBIND, 7). +-define(SOCKET_OPT_IP_FREEBIND, 7). %% -define(SOCKET_OPT_IP_HDRINCL, 8). -define(SOCKET_OPT_IP_MINTTL, 9). %% -define(SOCKET_OPT_IP_MSFILTER, 10). @@ -2177,6 +2181,8 @@ enc_setopt_value(ip, drop_source_membership, #{multiaddr := MA, (is_tuple(IF) andalso (size(IF) =:= 4)) andalso (is_tuple(SA) andalso (size(SA) =:= 4)) -> V; +enc_setopt_value(ip, freebind, V, _D, _T, _P) when is_boolean(V) -> + V; enc_setopt_value(ip, minttl, V, _D, _T, _P) when is_integer(V) -> V; enc_setopt_value(ip, mtu_discover, V, _D, _T, _P) @@ -2358,6 +2364,9 @@ enc_setopt_value(sctp, rtoinfo, #{assoc_id := AssocId, enc_setopt_value(sctp = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); +enc_setopt_value(raw = L, Opt, _V, _D, _T, _P) -> + not_supported({L, Opt}); + %% Is this correct? What about getopt? enc_setopt_value(L, Opt, V, _, _, _) when is_integer(L) andalso is_integer(Opt) andalso is_binary(V) -> @@ -2574,8 +2583,8 @@ enc_sockopt_key(ip = _L, drop_membership = _Opt, set = _Dir, _D, _T, _P) -> enc_sockopt_key(ip = _L, drop_source_membership = _Opt, set = _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP; %% Linux only? -enc_sockopt_key(ip = L, free_bind = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, freebind = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IP_FREEBIND; enc_sockopt_key(ip = L, hdrincl = Opt, _Dir, _D, raw = _T, _P) -> not_supported({L, Opt}); %% FreeBSD only? -- cgit v1.2.3 From 31ef72ceda0bf5bba902bf18f3b445950516d6af Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 23 Jul 2018 10:57:25 +0200 Subject: [socket-nif] Add support for socket (level ip) option recvopts Added support for ip level socket option RECVOPTS. OTP-14831 --- erts/preloaded/src/socket.erl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 329223c443..49c7a3f56d 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -614,7 +614,7 @@ %% -define(SOCKET_OPT_IP_RECVERR, 20). -define(SOCKET_OPT_IP_RECVIF, 21). %% -define(SOCKET_OPT_IP_RECVDSTADDR, 22). -%% -define(SOCKET_OPT_IP_RECVOPTS, 23). +-define(SOCKET_OPT_IP_RECVOPTS, 23). %% -define(SOCKET_OPT_IP_RECVORIGDSTADDR, 24). -define(SOCKET_OPT_IP_RECVTOS, 25). -define(SOCKET_OPT_IP_RECVTTL, 26). @@ -2210,6 +2210,9 @@ enc_setopt_value(ip, nodefrag, V, _D, _T, _P) enc_setopt_value(ip, recvif, V, _D, _T, _P) when is_boolean(V) -> V; +enc_setopt_value(ip, recvopts, V, _D, _T, _P) + when is_boolean(V) -> + V; enc_setopt_value(ip, recvtos, V, _D, _T, _P) when is_boolean(V) -> V; @@ -2619,8 +2622,8 @@ enc_sockopt_key(ip = _L, recvif = _Opt, _Dir, _D, T, _P) ?SOCKET_OPT_IP_RECVIF; enc_sockopt_key(ip = L, recvdstaddr = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(ip = L, recvopts = Opt, _Dir, _D, T, _P) when (T =/= stream) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, recvopts = _Opt, _Dir, _D, T, _P) when (T =/= stream) -> + ?SOCKET_OPT_IP_RECVOPTS; enc_sockopt_key(ip = L, recvorigdstaddr = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ip, recvtos = _Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From ba4b0ff7bf355a4fec434792495945872bb3efc5 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 23 Jul 2018 12:41:36 +0200 Subject: [socket-nif] Updated types for [recv|send]msg Updated and added types for the sendmsg and recvmsg functions. OTP-14831 --- erts/preloaded/src/socket.erl | 52 +++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 24 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 49c7a3f56d..e9b9f44a2c 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -102,7 +102,10 @@ sctp_rtoinfo/0, - msg_hdr/0, + msghdr_flag/0, + msghdr_flags/0, + msghdr/0, + cmsghdr/0, uint8/0, uint16/0, @@ -485,26 +488,28 @@ -type shutdown_how() :: read | write | read_write. -%% This is just a place-holder --record(msg_hdr, - { - %% Optional address - %% On an unconnected socket this is used to specify the target - %% address for a datagram. - %% For a connected socket, this field should be specifiedset to []. - name :: list(), - - %% Scatter/gather array - iov :: [binary()], % iovec(), - - %% Ancillary (control) data - ctrl :: binary(), - - %% Unused - flags = [] :: list() - }). --type msg_hdr() :: #msg_hdr{}. - +%% These are just place-holder(s) - used by the sendmsg/recvmsg functions... +-type msghdr_flag() :: eor | trunc | ctrunc | oob | errqueue. +-type msghdr_flags() :: [msghdr_flag()]. +-type msghdr() :: #{ + %% *Optional* target address + %% *If* this field is specified for an unconnected + %% socket, then it will be used as destination for the + %% datagram. + target => sockaddr(), + + iov => [binary()], + + ctrl => cmsghdr(), + + %% Only valid with recvmsg + flags => msghdr_flags() + }. +-type cmsghdr() :: #{ + level => protocol(), + type => integer(), + data => binary() + }. -define(SOCKET_DOMAIN_LOCAL, 1). -define(SOCKET_DOMAIN_UNIX, ?SOCKET_DOMAIN_LOCAL). @@ -1721,11 +1726,10 @@ do_recvfrom(SockRef, BufSz, EFlags, Timeout) -> %% --------------------------------------------------------------------------- %% -%% -spec recvmsg(Socket, [out] MsgHdr, Flags) -> {ok, Data} | {error, Reason} when +%% -spec recvmsg(Socket, Flags) -> {ok, MsgHdr} | {error, Reason} when %% Socket :: socket(), -%% MsgHdr :: msg_hdr(), +%% MsgHdr :: msghdr(), %% Flags :: recv_flags(), -%% Data :: binary(), %% Reason :: term(). -- cgit v1.2.3 From 2f99a47953404a18965fe6fe434593ee851e8677 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 23 Jul 2018 14:26:25 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option multicast_hops Added support for the IPv6 socket option MULTICAST_HOPS. OTP-14831. --- erts/preloaded/src/socket.erl | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index e9b9f44a2c..fd273951bd 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -649,7 +649,7 @@ %% -define(SOCKET_OPT_IPV6_LEAVE_GROUP, 16). -define(SOCKET_OPT_IPV6_MTU, 17). -define(SOCKET_OPT_IPV6_MTU_DISCOVER, 18). -%% -define(SOCKET_OPT_IPV6_MULTICAST_HOPS, 19). +-define(SOCKET_OPT_IPV6_MULTICAST_HOPS, 19). %% -define(SOCKET_OPT_IPV6_MULTICAST_IF, 20). %% -define(SOCKET_OPT_IPV6_MULTICAST_LOOP, 21). %% -define(SOCKET_OPT_IPV6_PORTRANGE, 22). @@ -2268,6 +2268,12 @@ enc_setopt_value(ipv6, mtu_discover, V, _D, _T, _P) (V =:= probe) orelse is_integer(V) -> V; +enc_setopt_value(ipv6, multicast_hops, V, _D, _T, _P) + when (V =:= default) -> + -1; +enc_setopt_value(ipv6, multicast_hops, V, _D, _T, _P) + when is_integer(V) andalso (V >= 0) andalso (V =< 255) -> + V; enc_setopt_value(ipv6, v6only, V, _D, _T, _P) when is_boolean(V) -> V; enc_setopt_value(ipv6 = L, Opt, V, _D, _T, _P) -> @@ -2692,8 +2698,8 @@ enc_sockopt_key(ipv6 = _L, mtu = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IPV6_MTU; enc_sockopt_key(ipv6 = _L, mtu_discover = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IPV6_MTU_DISCOVER; -enc_sockopt_key(ipv6 = L, multicast_hops = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ipv6 = _L, multicast_hops = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IPV6_MULTICAST_HOPS; enc_sockopt_key(ipv6 = L, multicast_if = Opt, _Dir, _D, T, _P) when (T =:= dgram) orelse (T =:= raw) -> not_supported({L, Opt}); -- cgit v1.2.3 From c077834b1465a8285f0c18e1d164c812aaadac9c Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 23 Jul 2018 15:02:39 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option multicast_if Added support for the IPv6 socket option MULTICAST_IF. OTP-14831. --- erts/preloaded/src/socket.erl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index fd273951bd..02232ebd68 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -650,7 +650,7 @@ -define(SOCKET_OPT_IPV6_MTU, 17). -define(SOCKET_OPT_IPV6_MTU_DISCOVER, 18). -define(SOCKET_OPT_IPV6_MULTICAST_HOPS, 19). -%% -define(SOCKET_OPT_IPV6_MULTICAST_IF, 20). +-define(SOCKET_OPT_IPV6_MULTICAST_IF, 20). %% -define(SOCKET_OPT_IPV6_MULTICAST_LOOP, 21). %% -define(SOCKET_OPT_IPV6_PORTRANGE, 22). %% -define(SOCKET_OPT_IPV6_PKTINFO, 23). @@ -2274,6 +2274,9 @@ enc_setopt_value(ipv6, multicast_hops, V, _D, _T, _P) enc_setopt_value(ipv6, multicast_hops, V, _D, _T, _P) when is_integer(V) andalso (V >= 0) andalso (V =< 255) -> V; +enc_setopt_value(ipv6, multicast_if, V, _D, _T, _P) + when is_integer(V) -> + V; enc_setopt_value(ipv6, v6only, V, _D, _T, _P) when is_boolean(V) -> V; enc_setopt_value(ipv6 = L, Opt, V, _D, _T, _P) -> @@ -2700,9 +2703,9 @@ enc_sockopt_key(ipv6 = _L, mtu_discover = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IPV6_MTU_DISCOVER; enc_sockopt_key(ipv6 = _L, multicast_hops = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IPV6_MULTICAST_HOPS; -enc_sockopt_key(ipv6 = L, multicast_if = Opt, _Dir, _D, T, _P) +enc_sockopt_key(ipv6 = _L, multicast_if = _Opt, _Dir, _D, T, _P) when (T =:= dgram) orelse (T =:= raw) -> - not_supported({L, Opt}); + ?SOCKET_OPT_IPV6_MULTICAST_IF; enc_sockopt_key(ipv6 = L, multicast_loop = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = L, portrange = Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 4d14b84183c3c17f0ec03bf3631f1fd8575f07b9 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 23 Jul 2018 17:36:16 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option multicast_loop Added support for the IPv6 socket option MULTICAST_LOOP. OTP-14831. --- erts/preloaded/src/socket.erl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 02232ebd68..7f160207b1 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -651,7 +651,7 @@ -define(SOCKET_OPT_IPV6_MTU_DISCOVER, 18). -define(SOCKET_OPT_IPV6_MULTICAST_HOPS, 19). -define(SOCKET_OPT_IPV6_MULTICAST_IF, 20). -%% -define(SOCKET_OPT_IPV6_MULTICAST_LOOP, 21). +-define(SOCKET_OPT_IPV6_MULTICAST_LOOP, 21). %% -define(SOCKET_OPT_IPV6_PORTRANGE, 22). %% -define(SOCKET_OPT_IPV6_PKTINFO, 23). %% -define(SOCKET_OPT_IPV6_PKTOPTIONS, 24). @@ -2277,6 +2277,9 @@ enc_setopt_value(ipv6, multicast_hops, V, _D, _T, _P) enc_setopt_value(ipv6, multicast_if, V, _D, _T, _P) when is_integer(V) -> V; +enc_setopt_value(ipv6, multicast_loop, V, _D, _T, _P) + when is_boolean(V) -> + V; enc_setopt_value(ipv6, v6only, V, _D, _T, _P) when is_boolean(V) -> V; enc_setopt_value(ipv6 = L, Opt, V, _D, _T, _P) -> @@ -2706,8 +2709,8 @@ enc_sockopt_key(ipv6 = _L, multicast_hops = _Opt, _Dir, _D, _T, _P) -> enc_sockopt_key(ipv6 = _L, multicast_if = _Opt, _Dir, _D, T, _P) when (T =:= dgram) orelse (T =:= raw) -> ?SOCKET_OPT_IPV6_MULTICAST_IF; -enc_sockopt_key(ipv6 = L, multicast_loop = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ipv6 = _L, multicast_loop = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IPV6_MULTICAST_LOOP; enc_sockopt_key(ipv6 = L, portrange = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = L, pktinfo = Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From de82310c32063b8556add3fe7cf62b26eef27841 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 23 Jul 2018 18:26:42 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option recvpktinfo Added support for the IPv6 socket option RECVPKTINFO. This option is called PKTINFO on FreeBSD, so that value will also be accepted. OTP-14831. --- erts/preloaded/src/socket.erl | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 7f160207b1..7e9b61ba8f 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -376,10 +376,9 @@ multicast_if | multicast_loop | portrange | - pktinfo | pktoptions | recverr | - recvpktinfo | + recvpktinfo | pktinfo | recvtclass | router_alert | rthdr | @@ -653,17 +652,16 @@ -define(SOCKET_OPT_IPV6_MULTICAST_IF, 20). -define(SOCKET_OPT_IPV6_MULTICAST_LOOP, 21). %% -define(SOCKET_OPT_IPV6_PORTRANGE, 22). -%% -define(SOCKET_OPT_IPV6_PKTINFO, 23). -%% -define(SOCKET_OPT_IPV6_PKTOPTIONS, 24). -%% -define(SOCKET_OPT_IPV6_RECVERR, 25). -%% -define(SOCKET_OPT_IPV6_RECVPKTINFO, 26). -%% -define(SOCKET_OPT_IPV6_RECVTCLASS, 27). -%% -define(SOCKET_OPT_IPV6_ROUTER_ALERT, 28). -%% -define(SOCKET_OPT_IPV6_RTHDR, 29). -%% -define(SOCKET_OPT_IPV6_TCLASS, 30). -%% -define(SOCKET_OPT_IPV6_UNICAST_HOPS, 31). -%% -define(SOCKET_OPT_IPV6_USE_MIN_MTU, 32). --define(SOCKET_OPT_IPV6_V6ONLY, 33). +%% -define(SOCKET_OPT_IPV6_PKTOPTIONS, 23). +%% -define(SOCKET_OPT_IPV6_RECVERR, 24). +-define(SOCKET_OPT_IPV6_RECVPKTINFO, 25). % On FreeBSD: PKTINFO +%% -define(SOCKET_OPT_IPV6_RECVTCLASS, 26). +%% -define(SOCKET_OPT_IPV6_ROUTER_ALERT, 27). +%% -define(SOCKET_OPT_IPV6_RTHDR, 28). +%% -define(SOCKET_OPT_IPV6_TCLASS, 29). +%% -define(SOCKET_OPT_IPV6_UNICAST_HOPS, 30). +%% -define(SOCKET_OPT_IPV6_USE_MIN_MTU, 31). +-define(SOCKET_OPT_IPV6_V6ONLY, 32). -define(SOCKET_OPT_TCP_CONGESTION, 1). -define(SOCKET_OPT_TCP_CORK, 2). @@ -2280,6 +2278,10 @@ enc_setopt_value(ipv6, multicast_if, V, _D, _T, _P) enc_setopt_value(ipv6, multicast_loop, V, _D, _T, _P) when is_boolean(V) -> V; +enc_setopt_value(ipv6, Opt, V, _D, _T, _P) + when ((Opt =:= recvpktinfo) orelse (Opt =:= pktinfo)) andalso + is_boolean(V) -> + V; enc_setopt_value(ipv6, v6only, V, _D, _T, _P) when is_boolean(V) -> V; enc_setopt_value(ipv6 = L, Opt, V, _D, _T, _P) -> @@ -2713,14 +2715,14 @@ enc_sockopt_key(ipv6 = _L, multicast_loop = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IPV6_MULTICAST_LOOP; enc_sockopt_key(ipv6 = L, portrange = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(ipv6 = L, pktinfo = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); enc_sockopt_key(ipv6 = L, pktoptions = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = L, recverr = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(ipv6 = L, recvpktinfo = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ipv6 = _L, Opt, _Dir, _D, T, _P) + when ((Opt =:= recvpktinfo) orelse (Opt =:= pktinfo)) andalso + ((T =:= dgram) orelse (T =:= raw)) -> + ?SOCKET_OPT_IPV6_RECVPKTINFO; enc_sockopt_key(ipv6 = L, recvtclass = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = L, router_alert = Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From d7ca5ba9d8a7f094037878acfecb52b0bfbacc2e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 24 Jul 2018 10:25:25 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option rthdr Added support for the IPv6 socket option RTHDR. On FreeBSD this option requires superuser privileges to update. There is no mention of this on linux, but its still not possible to update (einval), so I assume that its the same there. OTP-14831. --- erts/preloaded/src/socket.erl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 7e9b61ba8f..d2267192e5 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -657,7 +657,7 @@ -define(SOCKET_OPT_IPV6_RECVPKTINFO, 25). % On FreeBSD: PKTINFO %% -define(SOCKET_OPT_IPV6_RECVTCLASS, 26). %% -define(SOCKET_OPT_IPV6_ROUTER_ALERT, 27). -%% -define(SOCKET_OPT_IPV6_RTHDR, 28). +-define(SOCKET_OPT_IPV6_RTHDR, 28). %% -define(SOCKET_OPT_IPV6_TCLASS, 29). %% -define(SOCKET_OPT_IPV6_UNICAST_HOPS, 30). %% -define(SOCKET_OPT_IPV6_USE_MIN_MTU, 31). @@ -2282,6 +2282,9 @@ enc_setopt_value(ipv6, Opt, V, _D, _T, _P) when ((Opt =:= recvpktinfo) orelse (Opt =:= pktinfo)) andalso is_boolean(V) -> V; +enc_setopt_value(ipv6, rthdr, V, _D, _T, _P) + when is_boolean(V) -> + V; enc_setopt_value(ipv6, v6only, V, _D, _T, _P) when is_boolean(V) -> V; enc_setopt_value(ipv6 = L, Opt, V, _D, _T, _P) -> @@ -2727,9 +2730,9 @@ enc_sockopt_key(ipv6 = L, recvtclass = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = L, router_alert = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(ipv6 = L, rthdr = Opt, set = _Dir, _D, T, _P) +enc_sockopt_key(ipv6 = _L, rthdr = _Opt, _Dir, _D, T, _P) when ((T =:= dgram) orelse (T =:= raw)) -> - not_supported({L, Opt}); + ?SOCKET_OPT_IPV6_RTHDR; enc_sockopt_key(ipv6 = L, tclass = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = L, unicast_hops = Opt, _Dir, _D, _T, _P) -> @@ -2739,7 +2742,7 @@ enc_sockopt_key(ipv6 = L, use_min_mtu = Opt, _Dir, _D, _T, _P) -> enc_sockopt_key(ipv6 = _L, v6only = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IPV6_V6ONLY; enc_sockopt_key(ipv6 = L, UnknownOpt, _Dir, _D, _T, _P) -> - unknown({L, UnknownOpt}); + unknown({L, UnknownOpt, _Dir, _D, _T, _P}); %% TCP socket options %% There are other options that would be useful; info, -- cgit v1.2.3 From 8b61022bdca354e541380391e3b0b2606f7af3f8 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 24 Jul 2018 11:08:20 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option authhdr & hopopts Added support for the IPv6 socket option(s) AUTHHDR and HOPOPTS. Its possible that the option is AUTHHDR is obsolete. It says so in the include files and when trying to get it (getsockopt) it returns with enoprotoopt. The option HOPOPTS returns with einval when calling setsockopt, so either you need to be a privileged user to update, or its not actually possible to update this option (even though it says nothing about that in the man page. It only talks about set). This is the same behaviour as with RTHDR and HOPLIMIT. On FreeBSD, it says that HOPOPTS requires superuser privileges. Needs furher checking. OTP-14831. --- erts/preloaded/src/socket.erl | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index d2267192e5..3c0dc6e95b 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -632,7 +632,7 @@ %% -define(SOCKET_OPT_IPV6_ADDFORM, 1). -define(SOCKET_OPT_IPV6_ADD_MEMBERSHIP, 2). -%% -define(SOCKET_OPT_IPV6_AUTHHDR, 3). +-define(SOCKET_OPT_IPV6_AUTHHDR, 3). % Obsolete? %% -define(SOCKET_OPT_IPV6_AUTH_LEVEL, 4). %% -define(SOCKET_OPT_IPV6_CHECKSUM, 5). -define(SOCKET_OPT_IPV6_DROP_MEMBERSHIP, 6). @@ -642,7 +642,7 @@ %% -define(SOCKET_OPT_IPV6_FAITH, 10). %% -define(SOCKET_OPT_IPV6_FLOWINFO, 11). -define(SOCKET_OPT_IPV6_HOPLIMIT, 12). -%% -define(SOCKET_OPT_IPV6_HOPOPTS, 13). +-define(SOCKET_OPT_IPV6_HOPOPTS, 13). %% -define(SOCKET_OPT_IPV6_IPCOMP_LEVEL, 14). %% -define(SOCKET_OPT_IPV6_JOIN_GROUP, 15). %% -define(SOCKET_OPT_IPV6_LEAVE_GROUP, 16). @@ -2249,6 +2249,12 @@ enc_setopt_value(ipv6, add_membership, #{multiaddr := MA, when ((is_tuple(MA) andalso (size(MA) =:= 8)) andalso (is_integer(IF) andalso (IF >= 0))) -> V; +%% Is this obsolete? When get, the result is enoprotoopt and in the +%% header file it says 'obsolete'... +%% But there might be (old?) versions of linux where it still works... +enc_setopt_value(ipv6, authhdr, V, _D, T, _P) + when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) -> + V; enc_setopt_value(ipv6, drop_membership, #{multiaddr := MA, interface := IF} = V, _D, _T, _P) when ((is_tuple(MA) andalso (size(MA) =:= 8)) andalso @@ -2257,6 +2263,9 @@ enc_setopt_value(ipv6, drop_membership, #{multiaddr := MA, enc_setopt_value(ipv6, hoplimit, V, _D, T, _P) when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) -> V; +enc_setopt_value(ipv6, hopopts, V, _D, T, _P) + when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) -> + V; enc_setopt_value(ipv6, mtu, V, _D, _T, _P) when is_integer(V) -> V; enc_setopt_value(ipv6, mtu_discover, V, _D, _T, _P) @@ -2282,8 +2291,8 @@ enc_setopt_value(ipv6, Opt, V, _D, _T, _P) when ((Opt =:= recvpktinfo) orelse (Opt =:= pktinfo)) andalso is_boolean(V) -> V; -enc_setopt_value(ipv6, rthdr, V, _D, _T, _P) - when is_boolean(V) -> +enc_setopt_value(ipv6, rthdr, V, _D, T, _P) + when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) -> V; enc_setopt_value(ipv6, v6only, V, _D, _T, _P) when is_boolean(V) -> V; @@ -2674,9 +2683,9 @@ enc_sockopt_key(ipv6 = L, addrform = Opt, set = _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6, add_membership = _Opt, set = _Dir, _D, _T, _P) -> ?SOCKET_OPT_IPV6_ADD_MEMBERSHIP; -enc_sockopt_key(ipv6 = L, authhdr = Opt, set = _Dir, _D, T, _P) +enc_sockopt_key(ipv6 = _L, authhdr = _Opt, _Dir, _D, T, _P) when ((T =:= dgram) orelse (T =:= raw)) -> - not_supported({L, Opt}); + ?SOCKET_OPT_IPV6_AUTHHDR; enc_sockopt_key(ipv6 = L, auth_level = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = L, checksum = Opt, _Dir, _D, _T, _P) -> @@ -2693,12 +2702,12 @@ enc_sockopt_key(ipv6 = L, esp_network_level = Opt, _Dir, _D, _T, _P) -> enc_sockopt_key(ipv6 = L, flowinfo = Opt, set = _Dir, _D, T, _P) when (T =:= dgram) orelse (T =:= raw) -> not_supported({L, Opt}); -enc_sockopt_key(ipv6, hoplimit = _Opt, _Dir, set = _D, T, _P) +enc_sockopt_key(ipv6, hoplimit = _Opt, _Dir, _D, T, _P) when (T =:= dgram) orelse (T =:= raw) -> ?SOCKET_OPT_IPV6_HOPLIMIT; -enc_sockopt_key(ipv6 = L, hopopts = Opt, set = _Dir, _D, T, _P) +enc_sockopt_key(ipv6 = _L, hopopts = _Opt, _Dir, _D, T, _P) when ((T =:= dgram) orelse (T =:= raw)) -> - not_supported({L, Opt}); + ?SOCKET_OPT_IPV6_HOPOPTS; enc_sockopt_key(ipv6 = L, ipcomp_level = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = L, join_group = Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 9ca6de6efbe844bcf7dc996cfcf51bcd50325007 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 24 Jul 2018 11:49:12 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option dstopts Added support for the IPv6 socket option(s) DSTOPTS. The option returns with einval when calling setsockopt, so either you need to be a privileged user to update, or its not actually possible to update this option (even though it says nothing about that in the man page. It only talks about set). This is the same behaviour as with RTHDR and HOPLIMIT. On FreeBSD, it says that HOPOPTS requires superuser privileges. Needs furher checking. OTP-14831. --- erts/preloaded/src/socket.erl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 3c0dc6e95b..2bab42267a 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -636,7 +636,7 @@ %% -define(SOCKET_OPT_IPV6_AUTH_LEVEL, 4). %% -define(SOCKET_OPT_IPV6_CHECKSUM, 5). -define(SOCKET_OPT_IPV6_DROP_MEMBERSHIP, 6). -%% -define(SOCKET_OPT_IPV6_DSTOPTS, 7). +-define(SOCKET_OPT_IPV6_DSTOPTS, 7). %% -define(SOCKET_OPT_IPV6_ESP_TRANS_LEVEL, 8). %% -define(SOCKET_OPT_IPV6_ESP_NETWORK_LEVEL, 9). %% -define(SOCKET_OPT_IPV6_FAITH, 10). @@ -2255,6 +2255,9 @@ enc_setopt_value(ipv6, add_membership, #{multiaddr := MA, enc_setopt_value(ipv6, authhdr, V, _D, T, _P) when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) -> V; +enc_setopt_value(ipv6, dstopts, V, _D, T, _P) + when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) -> + V; enc_setopt_value(ipv6, drop_membership, #{multiaddr := MA, interface := IF} = V, _D, _T, _P) when ((is_tuple(MA) andalso (size(MA) =:= 8)) andalso @@ -2692,14 +2695,14 @@ enc_sockopt_key(ipv6 = L, checksum = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6, drop_membership = _Opt, set = _Dir, _D, _T, _P) -> ?SOCKET_OPT_IPV6_DROP_MEMBERSHIP; -enc_sockopt_key(ipv6 = L, dstopts = Opt, set = _Dir, _D, T, _P) +enc_sockopt_key(ipv6 = _L, dstopts = _Opt, _Dir, _D, T, _P) when (T =:= dgram) orelse (T =:= raw) -> - not_supported({L, Opt}); + ?SOCKET_OPT_IPV6_DSTOPTS; enc_sockopt_key(ipv6 = L, esp_trans_level = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = L, esp_network_level = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(ipv6 = L, flowinfo = Opt, set = _Dir, _D, T, _P) +enc_sockopt_key(ipv6 = L, flowinfo = Opt, _Dir, _D, T, _P) when (T =:= dgram) orelse (T =:= raw) -> not_supported({L, Opt}); enc_sockopt_key(ipv6, hoplimit = _Opt, _Dir, _D, T, _P) -- cgit v1.2.3 From e54642b537177941ff361b1eebdec10e02cfc22d Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 24 Jul 2018 12:05:00 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option flowinfo Added support for the IPv6 socket option(s) FLOWINFO. The option returns with einval when calling setsockopt, so either you need to be a privileged user to update, or its not actually possible to update this option (even though it says nothing about that in the man page. It only talks about set). This is the same behaviour as with DSTOPTS. Needs furher checking. OTP-14831. --- erts/preloaded/src/socket.erl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 2bab42267a..95bd5ce094 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -640,7 +640,7 @@ %% -define(SOCKET_OPT_IPV6_ESP_TRANS_LEVEL, 8). %% -define(SOCKET_OPT_IPV6_ESP_NETWORK_LEVEL, 9). %% -define(SOCKET_OPT_IPV6_FAITH, 10). -%% -define(SOCKET_OPT_IPV6_FLOWINFO, 11). +-define(SOCKET_OPT_IPV6_FLOWINFO, 11). -define(SOCKET_OPT_IPV6_HOPLIMIT, 12). -define(SOCKET_OPT_IPV6_HOPOPTS, 13). %% -define(SOCKET_OPT_IPV6_IPCOMP_LEVEL, 14). @@ -2263,6 +2263,9 @@ enc_setopt_value(ipv6, drop_membership, #{multiaddr := MA, when ((is_tuple(MA) andalso (size(MA) =:= 8)) andalso (is_integer(IF) andalso (IF >= 0))) -> V; +enc_setopt_value(ipv6, flowinfo, V, _D, T, _P) + when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) -> + V; enc_setopt_value(ipv6, hoplimit, V, _D, T, _P) when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) -> V; @@ -2702,9 +2705,9 @@ enc_sockopt_key(ipv6 = L, esp_trans_level = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = L, esp_network_level = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(ipv6 = L, flowinfo = Opt, _Dir, _D, T, _P) +enc_sockopt_key(ipv6 = _L, flowinfo = _Opt, _Dir, _D, T, _P) when (T =:= dgram) orelse (T =:= raw) -> - not_supported({L, Opt}); + ?SOCKET_OPT_IPV6_DSTOPTS; enc_sockopt_key(ipv6, hoplimit = _Opt, _Dir, _D, T, _P) when (T =:= dgram) orelse (T =:= raw) -> ?SOCKET_OPT_IPV6_HOPLIMIT; -- cgit v1.2.3 From b598160c2f1162658ea948284aee5b53951a3b9e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 24 Jul 2018 12:29:22 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option unicast_hops Added support for the IPv6 socket option UNICAST_HOPS. OTP-14831. --- erts/preloaded/src/socket.erl | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 95bd5ce094..3bc3c7dc28 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -659,7 +659,7 @@ %% -define(SOCKET_OPT_IPV6_ROUTER_ALERT, 27). -define(SOCKET_OPT_IPV6_RTHDR, 28). %% -define(SOCKET_OPT_IPV6_TCLASS, 29). -%% -define(SOCKET_OPT_IPV6_UNICAST_HOPS, 30). +-define(SOCKET_OPT_IPV6_UNICAST_HOPS, 30). %% -define(SOCKET_OPT_IPV6_USE_MIN_MTU, 31). -define(SOCKET_OPT_IPV6_V6ONLY, 32). @@ -2300,6 +2300,12 @@ enc_setopt_value(ipv6, Opt, V, _D, _T, _P) enc_setopt_value(ipv6, rthdr, V, _D, T, _P) when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) -> V; +enc_setopt_value(ipv6, unicast_hops, V, _D, _T, _P) + when (V =:= default) -> + -1; +enc_setopt_value(ipv6, unicast_hops, V, _D, _T, _P) + when is_integer(V) andalso (V >= 0) andalso (V =< 255) -> + V; enc_setopt_value(ipv6, v6only, V, _D, _T, _P) when is_boolean(V) -> V; enc_setopt_value(ipv6 = L, Opt, V, _D, _T, _P) -> @@ -2750,8 +2756,8 @@ enc_sockopt_key(ipv6 = _L, rthdr = _Opt, _Dir, _D, T, _P) ?SOCKET_OPT_IPV6_RTHDR; enc_sockopt_key(ipv6 = L, tclass = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(ipv6 = L, unicast_hops = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ipv6 = _L, unicast_hops = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IPV6_UNICAST_HOPS; enc_sockopt_key(ipv6 = L, use_min_mtu = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = _L, v6only = _Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 7d5b6e7bf640eb5d64679e3bf7b440b8e21e3a4d Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 24 Jul 2018 15:03:36 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option router_alert Added support for the IPv6 socket option ROUTER_ALERT. Only supported for raw sockets. OTP-14831. --- erts/preloaded/src/socket.erl | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 3bc3c7dc28..82a51ee245 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -128,7 +128,10 @@ -type type() :: stream | dgram | raw | rdm | seqpacket. %% We support only a subset of all protocols: --type protocol() :: ip | tcp | udp | sctp. +%% Note that the '{raw, integer()}' construct is intended +%% to be used with type = raw. +%% Note also that only the "superuser" can create a raw socket. +-type protocol() :: ip | tcp | udp | sctp | icmp | igmp | {raw, integer()}. -type port_number() :: 0..65535. @@ -525,6 +528,8 @@ -define(SOCKET_PROTOCOL_TCP, 2). -define(SOCKET_PROTOCOL_UDP, 3). -define(SOCKET_PROTOCOL_SCTP, 4). +-define(SOCKET_PROTOCOL_ICMP, 5). +-define(SOCKET_PROTOCOL_IGMP, 6). -define(SOCKET_LISTEN_BACKLOG_DEFAULT, 5). @@ -656,7 +661,7 @@ %% -define(SOCKET_OPT_IPV6_RECVERR, 24). -define(SOCKET_OPT_IPV6_RECVPKTINFO, 25). % On FreeBSD: PKTINFO %% -define(SOCKET_OPT_IPV6_RECVTCLASS, 26). -%% -define(SOCKET_OPT_IPV6_ROUTER_ALERT, 27). +-define(SOCKET_OPT_IPV6_ROUTER_ALERT, 27). -define(SOCKET_OPT_IPV6_RTHDR, 28). %% -define(SOCKET_OPT_IPV6_TCLASS, 29). -define(SOCKET_OPT_IPV6_UNICAST_HOPS, 30). @@ -2004,7 +2009,8 @@ enc_type(_, raw) -> ?SOCKET_TYPE_RAW; enc_type(_, seqpacket) -> ?SOCKET_TYPE_SEQPACKET; enc_type(_, Type) -> throw({error, {invalid_type, Type}}). --spec enc_protocol(Type, Protocol) -> non_neg_integer() when +-spec enc_protocol(Type, Protocol) -> non_neg_integer() | + {raw, non_neg_integer()} when Type :: type(), Protocol :: protocol(). @@ -2012,7 +2018,11 @@ enc_protocol(dgram, ip) -> ?SOCKET_PROTOCOL_IP; enc_protocol(stream, tcp) -> ?SOCKET_PROTOCOL_TCP; enc_protocol(dgram, udp) -> ?SOCKET_PROTOCOL_UDP; enc_protocol(seqpacket, sctp) -> ?SOCKET_PROTOCOL_SCTP; -enc_protocol(Type, Proto) -> throw({error, {invalid_protocol, {Type, Proto}}}). +enc_protocol(raw, icmp) -> ?SOCKET_PROTOCOL_ICMP; +enc_protocol(raw, igmp) -> ?SOCKET_PROTOCOL_IGMP; +enc_protocol(raw, {raw, P} = RAW) when is_integer(P) -> RAW; +enc_protocol(Type, Proto) -> + throw({error, {invalid_protocol, {Type, Proto}}}). -spec enc_send_flags(Flags) -> non_neg_integer() when @@ -2297,6 +2307,9 @@ enc_setopt_value(ipv6, Opt, V, _D, _T, _P) when ((Opt =:= recvpktinfo) orelse (Opt =:= pktinfo)) andalso is_boolean(V) -> V; +enc_setopt_value(ipv6, router_alert, V, _D, T, _P) + when is_integer(V) andalso (T =:= raw) -> + V; enc_setopt_value(ipv6, rthdr, V, _D, T, _P) when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) -> V; @@ -2749,8 +2762,8 @@ enc_sockopt_key(ipv6 = _L, Opt, _Dir, _D, T, _P) ?SOCKET_OPT_IPV6_RECVPKTINFO; enc_sockopt_key(ipv6 = L, recvtclass = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(ipv6 = L, router_alert = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ipv6 = _L, router_alert = _Opt, _Dir, _D, T, _P) when (T =:= raw) -> + ?SOCKET_OPT_IPV6_ROUTER_ALERT; enc_sockopt_key(ipv6 = _L, rthdr = _Opt, _Dir, _D, T, _P) when ((T =:= dgram) orelse (T =:= raw)) -> ?SOCKET_OPT_IPV6_RTHDR; -- cgit v1.2.3 From b9237c96b2b86c82bb128625cc532b3528222560 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 24 Jul 2018 16:11:32 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option addrform Added support for the IPv6 socket option ADDRFORM. Only allowed for IPv6 sockets that are connected and bound to a v4-mapped-on-v6 address. OTP-14831. --- erts/preloaded/src/socket.erl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 82a51ee245..0c3a17b54d 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -635,7 +635,7 @@ -define(SOCKET_OPT_IP_TTL, 32). -define(SOCKET_OPT_IP_UNBLOCK_SOURCE, 33). -%% -define(SOCKET_OPT_IPV6_ADDFORM, 1). +-define(SOCKET_OPT_IPV6_ADDRFORM, 1). -define(SOCKET_OPT_IPV6_ADD_MEMBERSHIP, 2). -define(SOCKET_OPT_IPV6_AUTHHDR, 3). % Obsolete? %% -define(SOCKET_OPT_IPV6_AUTH_LEVEL, 4). @@ -2254,6 +2254,8 @@ enc_setopt_value(ip, unblock_source, #{multiaddr := MA, enc_setopt_value(ip = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); +enc_setopt_value(ipv6, addrform, inet = V, _D, _T, _P) -> + enc_domain(V); enc_setopt_value(ipv6, add_membership, #{multiaddr := MA, interface := IF} = V, _D, _T, _P) when ((is_tuple(MA) andalso (size(MA) =:= 8)) andalso @@ -2704,8 +2706,8 @@ enc_sockopt_key(ip = L, UnknownOpt, _Dir, _D, _T, _P) -> unknown({L, UnknownOpt}); %% IPv6 socket options -enc_sockopt_key(ipv6 = L, addrform = Opt, set = _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ipv6 = _L, addrform = _Opt, set = _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IPV6_ADDRFORM; enc_sockopt_key(ipv6, add_membership = _Opt, set = _Dir, _D, _T, _P) -> ?SOCKET_OPT_IPV6_ADD_MEMBERSHIP; enc_sockopt_key(ipv6 = _L, authhdr = _Opt, _Dir, _D, T, _P) -- cgit v1.2.3 From 00a2425bde77ddb9ae4c03b8c4e5470064773981 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 24 Jul 2018 16:40:55 +0200 Subject: [socket-nif] Add support for socket (level ip) option recverr Added support for the IP socket option RECVERR. To actually make use of this option, we need the recvmsg function, which we don't have yet. Baby steps. OTP-14831. --- erts/preloaded/src/socket.erl | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 0c3a17b54d..0a101008c6 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -620,7 +620,7 @@ -define(SOCKET_OPT_IP_NODEFRAG, 17). %% -define(SOCKET_OPT_IP_OPTIONS, 18). %% -define(SOCKET_OPT_IP_PKTINFO, 19). -%% -define(SOCKET_OPT_IP_RECVERR, 20). +-define(SOCKET_OPT_IP_RECVERR, 20). -define(SOCKET_OPT_IP_RECVIF, 21). %% -define(SOCKET_OPT_IP_RECVDSTADDR, 22). -define(SOCKET_OPT_IP_RECVOPTS, 23). @@ -638,34 +638,34 @@ -define(SOCKET_OPT_IPV6_ADDRFORM, 1). -define(SOCKET_OPT_IPV6_ADD_MEMBERSHIP, 2). -define(SOCKET_OPT_IPV6_AUTHHDR, 3). % Obsolete? -%% -define(SOCKET_OPT_IPV6_AUTH_LEVEL, 4). -%% -define(SOCKET_OPT_IPV6_CHECKSUM, 5). +%% -define(SOCKET_OPT_IPV6_AUTH_LEVEL, 4). % FreeBSD +%% -define(SOCKET_OPT_IPV6_CHECKSUM, 5). % FreeBSD -define(SOCKET_OPT_IPV6_DROP_MEMBERSHIP, 6). -define(SOCKET_OPT_IPV6_DSTOPTS, 7). -%% -define(SOCKET_OPT_IPV6_ESP_TRANS_LEVEL, 8). -%% -define(SOCKET_OPT_IPV6_ESP_NETWORK_LEVEL, 9). -%% -define(SOCKET_OPT_IPV6_FAITH, 10). +%% -define(SOCKET_OPT_IPV6_ESP_TRANS_LEVEL, 8). % FreeBSD +%% -define(SOCKET_OPT_IPV6_ESP_NETWORK_LEVEL, 9). % FreeBSD +%% -define(SOCKET_OPT_IPV6_FAITH, 10). % FreeBSD -define(SOCKET_OPT_IPV6_FLOWINFO, 11). -define(SOCKET_OPT_IPV6_HOPLIMIT, 12). -define(SOCKET_OPT_IPV6_HOPOPTS, 13). -%% -define(SOCKET_OPT_IPV6_IPCOMP_LEVEL, 14). -%% -define(SOCKET_OPT_IPV6_JOIN_GROUP, 15). -%% -define(SOCKET_OPT_IPV6_LEAVE_GROUP, 16). +%% -define(SOCKET_OPT_IPV6_IPCOMP_LEVEL, 14). % FreeBSD +%% -define(SOCKET_OPT_IPV6_JOIN_GROUP, 15). % FreeBSD +%% -define(SOCKET_OPT_IPV6_LEAVE_GROUP, 16). % FreeBSD -define(SOCKET_OPT_IPV6_MTU, 17). -define(SOCKET_OPT_IPV6_MTU_DISCOVER, 18). -define(SOCKET_OPT_IPV6_MULTICAST_HOPS, 19). -define(SOCKET_OPT_IPV6_MULTICAST_IF, 20). -define(SOCKET_OPT_IPV6_MULTICAST_LOOP, 21). -%% -define(SOCKET_OPT_IPV6_PORTRANGE, 22). -%% -define(SOCKET_OPT_IPV6_PKTOPTIONS, 23). +%% -define(SOCKET_OPT_IPV6_PORTRANGE, 22). % FreeBSD +%% -define(SOCKET_OPT_IPV6_PKTOPTIONS, 23). % FreeBSD %% -define(SOCKET_OPT_IPV6_RECVERR, 24). -define(SOCKET_OPT_IPV6_RECVPKTINFO, 25). % On FreeBSD: PKTINFO %% -define(SOCKET_OPT_IPV6_RECVTCLASS, 26). -define(SOCKET_OPT_IPV6_ROUTER_ALERT, 27). -define(SOCKET_OPT_IPV6_RTHDR, 28). -%% -define(SOCKET_OPT_IPV6_TCLASS, 29). +%% -define(SOCKET_OPT_IPV6_TCLASS, 29). % FreeBSD -define(SOCKET_OPT_IPV6_UNICAST_HOPS, 30). -%% -define(SOCKET_OPT_IPV6_USE_MIN_MTU, 31). +%% -define(SOCKET_OPT_IPV6_USE_MIN_MTU, 31). % FreeBSD -define(SOCKET_OPT_IPV6_V6ONLY, 32). -define(SOCKET_OPT_TCP_CONGESTION, 1). @@ -2219,6 +2219,9 @@ enc_setopt_value(ip, multicast_ttl, V, _D, _T, _P) enc_setopt_value(ip, nodefrag, V, _D, _T, _P) when is_boolean(V) -> V; +enc_setopt_value(ip, recverr, V, _D, _T, _P) + when is_boolean(V) -> + V; enc_setopt_value(ip, recvif, V, _D, _T, _P) when is_boolean(V) -> V; @@ -2672,8 +2675,8 @@ enc_sockopt_key(ip = L, pktinfo = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); %% This require special code for accessing the errors. %% via calling the recvmsg with the MSG_ERRQUEUE flag set, -enc_sockopt_key(ip = L, recverr = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, recverr = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IP_RECVERR; enc_sockopt_key(ip = _L, recvif = _Opt, _Dir, _D, T, _P) when (T =:= dgram) orelse (T =:= raw) -> ?SOCKET_OPT_IP_RECVIF; -- cgit v1.2.3 From 4e24993aff4c5a0cb2bec96e5499131a660a79f9 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 24 Jul 2018 17:01:46 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option recverr Added support for the IPv6 socket option RECVERR. To actually make use of this option, we need the recvmsg function, which we don't have yet. Baby steps. OTP-14831. --- erts/preloaded/src/socket.erl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 0a101008c6..5695e45ac5 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -658,7 +658,7 @@ -define(SOCKET_OPT_IPV6_MULTICAST_LOOP, 21). %% -define(SOCKET_OPT_IPV6_PORTRANGE, 22). % FreeBSD %% -define(SOCKET_OPT_IPV6_PKTOPTIONS, 23). % FreeBSD -%% -define(SOCKET_OPT_IPV6_RECVERR, 24). +-define(SOCKET_OPT_IPV6_RECVERR, 24). -define(SOCKET_OPT_IPV6_RECVPKTINFO, 25). % On FreeBSD: PKTINFO %% -define(SOCKET_OPT_IPV6_RECVTCLASS, 26). -define(SOCKET_OPT_IPV6_ROUTER_ALERT, 27). @@ -2308,6 +2308,9 @@ enc_setopt_value(ipv6, multicast_if, V, _D, _T, _P) enc_setopt_value(ipv6, multicast_loop, V, _D, _T, _P) when is_boolean(V) -> V; +enc_setopt_value(ipv6, recverr, V, _D, _T, _P) + when is_boolean(V) -> + V; enc_setopt_value(ipv6, Opt, V, _D, _T, _P) when ((Opt =:= recvpktinfo) orelse (Opt =:= pktinfo)) andalso is_boolean(V) -> @@ -2759,8 +2762,8 @@ enc_sockopt_key(ipv6 = L, portrange = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ipv6 = L, pktoptions = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(ipv6 = L, recverr = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ipv6 = _L, recverr = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IPV6_RECVERR; enc_sockopt_key(ipv6 = _L, Opt, _Dir, _D, T, _P) when ((Opt =:= recvpktinfo) orelse (Opt =:= pktinfo)) andalso ((T =:= dgram) orelse (T =:= raw)) -> @@ -2781,7 +2784,7 @@ enc_sockopt_key(ipv6 = L, use_min_mtu = Opt, _Dir, _D, _T, _P) -> enc_sockopt_key(ipv6 = _L, v6only = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IPV6_V6ONLY; enc_sockopt_key(ipv6 = L, UnknownOpt, _Dir, _D, _T, _P) -> - unknown({L, UnknownOpt, _Dir, _D, _T, _P}); + unknown({L, UnknownOpt}); %% TCP socket options %% There are other options that would be useful; info, -- cgit v1.2.3 From 5d9de1cdc46c75117f15f1ab17f017cdb700eb4c Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 25 Jul 2018 11:28:49 +0200 Subject: [socket-nif] Add support for socket (level ip) option msfilter Added support for ip level socket option MSFILTER. This option has not been tested *in any way*... OTP-14831 --- erts/preloaded/src/socket.erl | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 5695e45ac5..acda4e3811 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -94,6 +94,8 @@ ip_mreq/0, ip_mreq_source/0, ip_pmtudisc/0, + ip_msfilter_mode/0, + ip_msfilter/0, ipv6_mreq/0, ipv6_pmtudisc/0, sctp_event_subscribe/0, @@ -196,6 +198,17 @@ -type ip_pmtudisc() :: want | dont | do | probe. +%% multiaddr: Multicast group address +%% interface: Address of local interface +%% mode: Filter mode +%% slist: List of source addresses +-type ip_msfilter_mode() :: include | exclude. + +-type ip_msfilter() :: #{multiaddr => ip4_address(), + interface => ip4_address(), + mode => ip_msfilter_mode(), + slist => [ip4_address()]}. + -type ipv6_mreq() :: #{multiaddr := ip6_address(), interface := non_neg_integer()}. @@ -604,13 +617,13 @@ -define(SOCKET_OPT_IP_ADD_MEMBERSHIP, 1). -define(SOCKET_OPT_IP_ADD_SOURCE_MEMBERSHIP, 2). -define(SOCKET_OPT_IP_BLOCK_SOURCE, 3). -%% -define(SOCKET_OPT_IP_DONTFRAG, 4). +%% -define(SOCKET_OPT_IP_DONTFRAG, 4). % FreeBSD -define(SOCKET_OPT_IP_DROP_MEMBERSHIP, 5). -define(SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP, 6). -define(SOCKET_OPT_IP_FREEBIND, 7). %% -define(SOCKET_OPT_IP_HDRINCL, 8). -define(SOCKET_OPT_IP_MINTTL, 9). -%% -define(SOCKET_OPT_IP_MSFILTER, 10). +-define(SOCKET_OPT_IP_MSFILTER, 10). -define(SOCKET_OPT_IP_MTU, 11). -define(SOCKET_OPT_IP_MTU_DISCOVER, 12). -define(SOCKET_OPT_IP_MULTICAST_ALL, 13). @@ -2197,6 +2210,18 @@ enc_setopt_value(ip, freebind, V, _D, _T, _P) when is_boolean(V) -> V; enc_setopt_value(ip, minttl, V, _D, _T, _P) when is_integer(V) -> V; +enc_setopt_value(ip, msfilter, null = V, _D, _T, _P) -> + V; +enc_setopt_value(ip, msfilter, #{multiaddr := MA, + interface := IF, + fmode := FMode, + slist := SL} = V, _D, _T, _P) + when (is_tuple(MA) andalso (size(MA) =:= 4)) andalso + (is_tuple(IF) andalso (size(IF) =:= 4)) andalso + ((FMode =:= include) orelse (FMode =:= exclude)) andalso + is_list(SL) -> + ensure_ip_msfilter_slist(SL), + V; enc_setopt_value(ip, mtu_discover, V, _D, _T, _P) when (V =:= want) orelse (V =:= dont) orelse @@ -2656,8 +2681,8 @@ enc_sockopt_key(ip = L, hdrincl = Opt, _Dir, _D, raw = _T, _P) -> %% FreeBSD only? enc_sockopt_key(ip = _L, minttl = _Opt, _Dir, _D, raw = _T, _P) -> ?SOCKET_OPT_IP_MINTTL; -enc_sockopt_key(ip = L, msfilter = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, msfilter = _Opt, set = _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IP_MSFILTER; enc_sockopt_key(ip = _L, mtu = _Opt, get = _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_MTU; enc_sockopt_key(ip = _L, mtu_discover = _Opt, _Dir, _D, _T, _P) -> @@ -2922,6 +2947,13 @@ enc_shutdown_how(read_write) -> %% %% =========================================================================== +ensure_ip_msfilter_slist(SL) -> + EnsureSA = fun(SA) when is_tuple(SA) andalso (size(SA) =:= 4) -> ok; + (_) -> einval() + end, + lists:foreach(EnsureSA, SL). + + ensure_sockaddr(#{family := inet} = SockAddr) -> maps:merge(?SOCKADDR_IN4_DEFAULTS, SockAddr); ensure_sockaddr(#{family := inet6} = SockAddr) -> -- cgit v1.2.3 From 587d3a9a76b6ef2c88b850d007d39d34c37b5825 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 25 Jul 2018 12:00:34 +0200 Subject: [socket-nif] Add support for socket (level ip) option hdrincl Added support for ip level socket option HDRINCL. As this option is raw only, it has not yet been tested! OTP-14831 --- erts/preloaded/src/socket.erl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index acda4e3811..4028fa9191 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -621,7 +621,7 @@ -define(SOCKET_OPT_IP_DROP_MEMBERSHIP, 5). -define(SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP, 6). -define(SOCKET_OPT_IP_FREEBIND, 7). -%% -define(SOCKET_OPT_IP_HDRINCL, 8). +-define(SOCKET_OPT_IP_HDRINCL, 8). -define(SOCKET_OPT_IP_MINTTL, 9). -define(SOCKET_OPT_IP_MSFILTER, 10). -define(SOCKET_OPT_IP_MTU, 11). @@ -631,7 +631,7 @@ -define(SOCKET_OPT_IP_MULTICAST_LOOP, 15). -define(SOCKET_OPT_IP_MULTICAST_TTL, 16). -define(SOCKET_OPT_IP_NODEFRAG, 17). -%% -define(SOCKET_OPT_IP_OPTIONS, 18). +%% -define(SOCKET_OPT_IP_OPTIONS, 18). % FreeBSD %% -define(SOCKET_OPT_IP_PKTINFO, 19). -define(SOCKET_OPT_IP_RECVERR, 20). -define(SOCKET_OPT_IP_RECVIF, 21). @@ -2208,6 +2208,8 @@ enc_setopt_value(ip, drop_source_membership, #{multiaddr := MA, V; enc_setopt_value(ip, freebind, V, _D, _T, _P) when is_boolean(V) -> V; +enc_setopt_value(ip, hdrincl, V, _D, _T, _P) when is_boolean(V) -> + V; enc_setopt_value(ip, minttl, V, _D, _T, _P) when is_integer(V) -> V; enc_setopt_value(ip, msfilter, null = V, _D, _T, _P) -> @@ -2676,9 +2678,8 @@ enc_sockopt_key(ip = _L, drop_source_membership = _Opt, set = _Dir, _D, _T, _P) %% Linux only? enc_sockopt_key(ip = _L, freebind = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_FREEBIND; -enc_sockopt_key(ip = L, hdrincl = Opt, _Dir, _D, raw = _T, _P) -> - not_supported({L, Opt}); -%% FreeBSD only? +enc_sockopt_key(ip = _L, hdrincl = _Opt, _Dir, _D, raw = _T, _P) -> + ?SOCKET_OPT_IP_HDRINCL; enc_sockopt_key(ip = _L, minttl = _Opt, _Dir, _D, raw = _T, _P) -> ?SOCKET_OPT_IP_MINTTL; enc_sockopt_key(ip = _L, msfilter = _Opt, set = _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 70a5b8d6a01b91a6044c6a5a0f8ed8919afd509b Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 25 Jul 2018 12:29:41 +0200 Subject: [socket-nif] Add support for socket (level ip) option pktinfo Added support for ip level socket option PKTINFO. This option requires sendmsg and/or recvmsg to actually use, so we cannot test this fully at the moment (although both set and get works). OTP-14831 --- erts/preloaded/src/socket.erl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 4028fa9191..28931e4ed0 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -632,7 +632,7 @@ -define(SOCKET_OPT_IP_MULTICAST_TTL, 16). -define(SOCKET_OPT_IP_NODEFRAG, 17). %% -define(SOCKET_OPT_IP_OPTIONS, 18). % FreeBSD -%% -define(SOCKET_OPT_IP_PKTINFO, 19). +-define(SOCKET_OPT_IP_PKTINFO, 19). -define(SOCKET_OPT_IP_RECVERR, 20). -define(SOCKET_OPT_IP_RECVIF, 21). %% -define(SOCKET_OPT_IP_RECVDSTADDR, 22). @@ -2246,6 +2246,9 @@ enc_setopt_value(ip, multicast_ttl, V, _D, _T, _P) enc_setopt_value(ip, nodefrag, V, _D, _T, _P) when is_boolean(V) -> V; +enc_setopt_value(ip, pktinfo, V, _D, _T, _P) + when is_boolean(V) -> + V; enc_setopt_value(ip, recverr, V, _D, _T, _P) when is_boolean(V) -> V; @@ -2700,8 +2703,8 @@ enc_sockopt_key(ip = _L, nodefrag = _Opt, _Dir, _D, raw = _T, _P) -> ?SOCKET_OPT_IP_NODEFRAG; enc_sockopt_key(ip = L, options = Opt, _Dir, _D, _T, _P) -> not_supported({Opt, L}); -enc_sockopt_key(ip = L, pktinfo = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, pktinfo = _Opt, _Dir, _D, dgram = _T, _P) -> + ?SOCKET_OPT_IP_PKTINFO; %% This require special code for accessing the errors. %% via calling the recvmsg with the MSG_ERRQUEUE flag set, enc_sockopt_key(ip = _L, recverr = _Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3 From 673367a0c17349a8b57dfad5dbc349c68417c6a5 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 25 Jul 2018 13:07:34 +0200 Subject: [socket-nif] Add support for socket (level ip) option transparent Added support for ip level socket option TRANSPARENT. OTP-14831 --- erts/preloaded/src/socket.erl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 28931e4ed0..c7589cbd48 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -644,7 +644,7 @@ -define(SOCKET_OPT_IP_ROUTER_ALERT, 28). %% -define(SOCKET_OPT_IP_SNDSRCADDR, 29). -define(SOCKET_OPT_IP_TOS, 30). -%% -define(SOCKET_OPT_IP_TRANSPARENT, 31). +-define(SOCKET_OPT_IP_TRANSPARENT, 31). -define(SOCKET_OPT_IP_TTL, 32). -define(SOCKET_OPT_IP_UNBLOCK_SOURCE, 33). @@ -2274,6 +2274,9 @@ enc_setopt_value(ip, tos, V, _D, _T, _P) (V =:= mincost) orelse is_integer(V) -> V; +enc_setopt_value(ip, transparent, V, _D, _T, _P) + when is_boolean(V) -> + V; enc_setopt_value(ip, ttl, V, _D, _T, _P) when is_integer(V) -> V; @@ -2731,8 +2734,8 @@ enc_sockopt_key(ip, router_alert = _Opt, _Dir, _D, raw = _T, _P) -> %% No such condition on linux (in the man page)... enc_sockopt_key(ip, tos = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_TOS; -enc_sockopt_key(ip = L, transparent = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, transparent = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IP_TRANSPARENT; enc_sockopt_key(ip, ttl = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_TTL; enc_sockopt_key(ip = _L, unblock_source = _Opt, set = _Dir, _D, _T, _P) -> -- cgit v1.2.3 From cb8877a5561ac64704337441936b62c8c87f8d13 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 25 Jul 2018 14:09:24 +0200 Subject: [socket-nif] Add support for socket (level ip) option retopts Added support for ip level socket option RETOPTS. OTP-14831 --- erts/preloaded/src/socket.erl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index c7589cbd48..e7438cd856 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -640,7 +640,7 @@ %% -define(SOCKET_OPT_IP_RECVORIGDSTADDR, 24). -define(SOCKET_OPT_IP_RECVTOS, 25). -define(SOCKET_OPT_IP_RECVTTL, 26). -%% -define(SOCKET_OPT_IP_RETOPTS, 27). +-define(SOCKET_OPT_IP_RETOPTS, 27). -define(SOCKET_OPT_IP_ROUTER_ALERT, 28). %% -define(SOCKET_OPT_IP_SNDSRCADDR, 29). -define(SOCKET_OPT_IP_TOS, 30). @@ -2264,6 +2264,9 @@ enc_setopt_value(ip, recvtos, V, _D, _T, _P) enc_setopt_value(ip, recvttl, V, _D, _T, _P) when is_boolean(V) -> V; +enc_setopt_value(ip, retopts, V, _D, _T, _P) + when is_boolean(V) -> + V; enc_setopt_value(ip, router_alert, V, _D, _T, _P) when is_integer(V) -> V; @@ -2725,8 +2728,8 @@ enc_sockopt_key(ip, recvtos = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_RECVTOS; enc_sockopt_key(ip = _L, recvttl = _Opt, _Dir, _D, T, _P) when (T =/= stream) -> ?SOCKET_OPT_IP_RECVTTL; -enc_sockopt_key(ip = L, retopts = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, retopts = _Opt, _Dir, _D, T, _P) when (T =/= stream) -> + ?SOCKET_OPT_IP_RETOPTS; enc_sockopt_key(ip, router_alert = _Opt, _Dir, _D, raw = _T, _P) -> ?SOCKET_OPT_IP_ROUTER_ALERT; %% On FreeBSD it specifies that this option is only valid -- cgit v1.2.3 From 91d965f2ddfb420100ba685a059ec84eba18d0f9 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 25 Jul 2018 18:06:48 +0200 Subject: [socket-nif] Updated socket type Updated the socket type. No longer store "stuff" that can be retrieved by other means (domain, type and protocol). OTP-14831 --- erts/preloaded/src/socket.erl | 116 ++++++++++++++++++++++++++++-------------- 1 file changed, 78 insertions(+), 38 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index e7438cd856..fcc052a25d 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -466,12 +466,8 @@ %% raw_socket_option() | %% plain_socket_option(). --type socket_info() :: #{domain => domain(), - type => type(), - protocol => protocol()}. --record(socket, {info :: socket_info(), - ref :: reference()}). -%% -opaque socket() :: {socket, socket_info(), reference()}. +-record(socket, {ref :: reference()}). + -opaque socket() :: #socket{}. -type accept_flags() :: [accept_flag()]. @@ -890,11 +886,7 @@ open(Domain, Type, Protocol0, Extra) when is_map(Extra) -> EProtocol = enc_protocol(Type, Protocol), case nif_open(EDomain, EType, EProtocol, Extra) of {ok, SockRef} -> - SocketInfo = #{domain => Domain, - type => Type, - protocol => Protocol}, - Socket = #socket{info = SocketInfo, - ref = SockRef}, + Socket = #socket{ref = SockRef}, {ok, Socket}; {error, _} = ERROR -> ERROR @@ -928,12 +920,17 @@ default_protocol(Protocol, _) -> Protocol. Addr :: any | loopback | sockaddr(), Reason :: term(). -bind(#socket{ref = SockRef, info = #{domain := inet}} = _Socket, Addr) - when ((Addr =:= any) orelse (Addr =:= loopback)) -> - nif_bind(SockRef, ?SOCKADDR_IN4_DEFAULT(Addr)); -bind(#socket{ref = SockRef, info = #{domain := inet6}} = _Socket, Addr) +bind(#socket{ref = SockRef}, Addr) when ((Addr =:= any) orelse (Addr =:= loopback)) -> - nif_bind(SockRef, ?SOCKADDR_IN6_DEFAULT(Addr)); + try which_domain(SockRef) of + inet -> + nif_bind(SockRef, ?SOCKADDR_IN4_DEFAULT(Addr)); + inet6 -> + nif_bind(SockRef, ?SOCKADDR_IN6_DEFAULT(Addr)) + catch + throw:ERROR -> + ERROR + end; bind(#socket{ref = SockRef} = _Socket, Addr) when is_map(Addr) -> try begin @@ -964,14 +961,13 @@ bind(#socket{ref = SockRef} = _Socket, Addr) when is_map(Addr) -> Action :: add | remove, Reason :: term(). -bind(#socket{ref = SockRef, - info = #{domain := Domain, - type := seqpacket, - protocol := sctp}} = _Socket, Addrs, Action) +bind(#socket{ref = SockRef}, Addrs, Action) when is_list(Addrs) andalso ((Action =:= add) orelse (Action =:= remove)) -> try begin - validate_addrs(Domain, Addrs), + ensure_type(seqpacket, which_type(SockRef)), + ensure_proto(sctp, which_protocol(SockRef)), + validate_addrs(which_domain(SockRef), Addrs), nif_bind(SockRef, Addrs, Action) end catch @@ -979,6 +975,21 @@ bind(#socket{ref = SockRef, ERROR end. +ensure_type(SockRef, Type) -> + case which_type(SockRef) of + Type -> + ok; + _InvalidType -> + einval() + end. + +ensure_proto(SockRef, Proto) -> + case which_protocol(SockRef) of + Proto -> + ok; + _InvalidProto -> + einval() + end. validate_addrs(inet = _Domain, Addrs) -> validate_inet_addrs(Addrs); @@ -1119,11 +1130,11 @@ accept(Socket) -> %% Do we really need this optimization? accept(_, Timeout) when is_integer(Timeout) andalso (Timeout =< 0) -> {error, timeout}; -accept(#socket{info = SI, ref = LSockRef}, Timeout) +accept(#socket{ref = LSockRef}, Timeout) when is_integer(Timeout) orelse (Timeout =:= infinity) -> - do_accept(LSockRef, SI, Timeout). + do_accept(LSockRef, Timeout). -do_accept(LSockRef, SI, Timeout) -> +do_accept(LSockRef, Timeout) -> TS = timestamp(Timeout), AccRef = make_ref(), case nif_accept(LSockRef, AccRef) of @@ -1138,18 +1149,17 @@ do_accept(LSockRef, SI, Timeout) -> %% message). %% %% - SocketInfo = #{domain => maps:get(domain, SI), - type => maps:get(type, SI), - protocol => maps:get(protocol, SI)}, - Socket = #socket{info = SocketInfo, - ref = SockRef}, + Socket = #socket{ref = SockRef}, {ok, Socket}; {error, eagain} -> + %% Each call is non-blocking, but even then it takes + %% *some* time, so just to be sure, recalculate before + %% the receive. NewTimeout = next_timeout(TS, Timeout), receive {select, LSockRef, AccRef, ready_input} -> - do_accept(LSockRef, SI, next_timeout(TS, Timeout)); + do_accept(LSockRef, next_timeout(TS, Timeout)); {nif_abort, AccRef, Reason} -> {error, Reason} @@ -1863,12 +1873,12 @@ shutdown(#socket{ref = SockRef}, How) -> Value :: term(), Reason :: term(). -setopt(#socket{info = Info, ref = SockRef}, Level, Key, Value) -> +setopt(#socket{ref = SockRef}, Level, Key, Value) -> try begin - Domain = maps:get(domain, Info), - Type = maps:get(type, Info), - Protocol = maps:get(protocol, Info), + Domain = which_domain(SockRef), + Type = which_type(SockRef), + Protocol = which_protocol(SockRef), {EIsEncoded, ELevel} = enc_setopt_level(Level), EKey = enc_setopt_key(Level, Key, Domain, Type, Protocol), EVal = enc_setopt_value(Level, Key, Value, Domain, Type, Protocol), @@ -1935,12 +1945,12 @@ setopt(#socket{info = Info, ref = SockRef}, Level, Key, Value) -> Value :: term(), Reason :: term(). -getopt(#socket{info = Info, ref = SockRef}, Level, Key) -> +getopt(#socket{ref = SockRef}, Level, Key) -> try begin - Domain = maps:get(domain, Info), - Type = maps:get(type, Info), - Protocol = maps:get(protocol, Info), + Domain = which_domain(SockRef), + Type = which_type(SockRef), + Protocol = which_protocol(SockRef), {EIsEncoded, ELevel} = enc_getopt_level(Level), EKey = enc_getopt_key(Level, Key, Domain, Type, Protocol), %% We may need to decode the value (for the same reason @@ -1964,6 +1974,36 @@ getopt(#socket{info = Info, ref = SockRef}, Level, Key) -> end. +%% These are internal "shortcut" functions for the options +%% domain, type and protocol. +which_domain(SockRef) -> + case nif_getopt(SockRef, true, + ?SOCKET_OPT_LEVEL_SOCKET, ?SOCKET_OPT_SOCK_DOMAIN) of + {ok, Domain} -> + Domain; + {error, _} = ERROR -> + throw(ERROR) + end. + + +which_type(SockRef) -> + case nif_getopt(SockRef, true, + ?SOCKET_OPT_LEVEL_SOCKET, ?SOCKET_OPT_SOCK_TYPE) of + {ok, Type} -> + Type; + {error, _} = ERROR -> + throw(ERROR) + end. + +which_protocol(SockRef) -> + case nif_getopt(SockRef, true, + ?SOCKET_OPT_LEVEL_SOCKET, ?SOCKET_OPT_SOCK_PROTOCOL) of + {ok, Type} -> + Type; + {error, _} = ERROR -> + throw(ERROR) + end. + %% =========================================================================== %% -- cgit v1.2.3 From 8ed757c8df2df54e19e67ca0a0734cd5a0f9ab23 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 26 Jul 2018 10:10:16 +0200 Subject: [socket-nif] Add support for socket (level ip) option recvorigdstaddr Added support for ip level socket option RECVORIGDSTADDR. This option requires recvmsg to actually use, so we cannot test this fully at the moment (although both set and get works). OTP-14831 --- erts/preloaded/src/socket.erl | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index fcc052a25d..68b7d3f4b0 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -629,11 +629,11 @@ -define(SOCKET_OPT_IP_NODEFRAG, 17). %% -define(SOCKET_OPT_IP_OPTIONS, 18). % FreeBSD -define(SOCKET_OPT_IP_PKTINFO, 19). --define(SOCKET_OPT_IP_RECVERR, 20). --define(SOCKET_OPT_IP_RECVIF, 21). -%% -define(SOCKET_OPT_IP_RECVDSTADDR, 22). +-define(SOCKET_OPT_IP_RECVDSTADDR, 20). % FreeBSD +-define(SOCKET_OPT_IP_RECVERR, 21). +-define(SOCKET_OPT_IP_RECVIF, 22). -define(SOCKET_OPT_IP_RECVOPTS, 23). -%% -define(SOCKET_OPT_IP_RECVORIGDSTADDR, 24). +-define(SOCKET_OPT_IP_RECVORIGDSTADDR, 24). -define(SOCKET_OPT_IP_RECVTOS, 25). -define(SOCKET_OPT_IP_RECVTTL, 26). -define(SOCKET_OPT_IP_RETOPTS, 27). @@ -2289,6 +2289,9 @@ enc_setopt_value(ip, nodefrag, V, _D, _T, _P) enc_setopt_value(ip, pktinfo, V, _D, _T, _P) when is_boolean(V) -> V; +enc_setopt_value(ip, recvdstaddr, V, _D, _T, _P) + when is_boolean(V) -> + V; enc_setopt_value(ip, recverr, V, _D, _T, _P) when is_boolean(V) -> V; @@ -2298,6 +2301,9 @@ enc_setopt_value(ip, recvif, V, _D, _T, _P) enc_setopt_value(ip, recvopts, V, _D, _T, _P) when is_boolean(V) -> V; +enc_setopt_value(ip, recvorigdstaddr, V, _D, _T, _P) + when is_boolean(V) -> + V; enc_setopt_value(ip, recvtos, V, _D, _T, _P) when is_boolean(V) -> V; @@ -2751,19 +2757,17 @@ enc_sockopt_key(ip = L, options = Opt, _Dir, _D, _T, _P) -> not_supported({Opt, L}); enc_sockopt_key(ip = _L, pktinfo = _Opt, _Dir, _D, dgram = _T, _P) -> ?SOCKET_OPT_IP_PKTINFO; -%% This require special code for accessing the errors. -%% via calling the recvmsg with the MSG_ERRQUEUE flag set, +enc_sockopt_key(ip = _L, recvdstaddr = _Opt, _Dir, _D, T, _P) when (T =:= dgram) -> + ?SOCKET_OPT_IP_RECVDSTADDR; enc_sockopt_key(ip = _L, recverr = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_RECVERR; enc_sockopt_key(ip = _L, recvif = _Opt, _Dir, _D, T, _P) when (T =:= dgram) orelse (T =:= raw) -> ?SOCKET_OPT_IP_RECVIF; -enc_sockopt_key(ip = L, recvdstaddr = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); enc_sockopt_key(ip = _L, recvopts = _Opt, _Dir, _D, T, _P) when (T =/= stream) -> ?SOCKET_OPT_IP_RECVOPTS; -enc_sockopt_key(ip = L, recvorigdstaddr = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, recvorigdstaddr = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IP_RECVORIGDSTADDR; enc_sockopt_key(ip, recvtos = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_RECVTOS; enc_sockopt_key(ip = _L, recvttl = _Opt, _Dir, _D, T, _P) when (T =/= stream) -> -- cgit v1.2.3 From 6b01561dc13a0152f56da0a2c61ad88236f87de7 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 26 Jul 2018 11:07:05 +0200 Subject: [socket-nif] Add support for socket (level ip) option sendsrcaddr Added support for ip level socket option SENDSRCADDR. This option requires sendmsg to actually use, so we cannot test this fully at the moment. OTP-14831 --- erts/preloaded/src/socket.erl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 68b7d3f4b0..1983c993a5 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -638,11 +638,11 @@ -define(SOCKET_OPT_IP_RECVTTL, 26). -define(SOCKET_OPT_IP_RETOPTS, 27). -define(SOCKET_OPT_IP_ROUTER_ALERT, 28). -%% -define(SOCKET_OPT_IP_SNDSRCADDR, 29). +-define(SOCKET_OPT_IP_SENDSRCADDR, 29). % FreeBSD -define(SOCKET_OPT_IP_TOS, 30). -define(SOCKET_OPT_IP_TRANSPARENT, 31). -define(SOCKET_OPT_IP_TTL, 32). --define(SOCKET_OPT_IP_UNBLOCK_SOURCE, 33). +-define(SOCKET_OPT_IP_UNBLOCK_SOURCE, 33). -define(SOCKET_OPT_IPV6_ADDRFORM, 1). -define(SOCKET_OPT_IPV6_ADD_MEMBERSHIP, 2). @@ -2316,6 +2316,9 @@ enc_setopt_value(ip, retopts, V, _D, _T, _P) enc_setopt_value(ip, router_alert, V, _D, _T, _P) when is_integer(V) -> V; +enc_setopt_value(ip, sendsrcaddr, V, _D, _T, _P) + when is_boolean(V) -> + V; enc_setopt_value(ip, tos, V, _D, _T, _P) when (V =:= lowdelay) orelse (V =:= throughput) orelse @@ -2776,6 +2779,8 @@ enc_sockopt_key(ip = _L, retopts = _Opt, _Dir, _D, T, _P) when (T =/= stream) -> ?SOCKET_OPT_IP_RETOPTS; enc_sockopt_key(ip, router_alert = _Opt, _Dir, _D, raw = _T, _P) -> ?SOCKET_OPT_IP_ROUTER_ALERT; +enc_sockopt_key(ip, sendsrcaddr = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IP_SENDSRCADDR; %% On FreeBSD it specifies that this option is only valid %% for stream, dgram and "some" raw sockets... %% No such condition on linux (in the man page)... -- cgit v1.2.3 From 165666d8b8b1b21ffaf43ac436cfc1657ba83649 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 30 Jul 2018 18:22:46 +0200 Subject: [socket-nif] Add support for recvmsg Added preliminary support for function recvmsg. At the moment this only works on *nix (Windows has another function, WSARecvMsg, which has a slightly different API). Also we have "no" cmsg decode at the moment (just level and type). OTP-14831 --- erts/preloaded/src/socket.erl | 80 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 11 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 1983c993a5..b9d1705d45 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -43,7 +43,7 @@ recv/1, recv/2, recv/3, recv/4, recvfrom/1, recvfrom/2, recvfrom/3, recvfrom/4, - %% recvmsg/4, + recvmsg/1, recvmsg/2, recvmsg/5, %% readv/3, close/1, @@ -500,24 +500,28 @@ -type shutdown_how() :: read | write | read_write. %% These are just place-holder(s) - used by the sendmsg/recvmsg functions... --type msghdr_flag() :: eor | trunc | ctrunc | oob | errqueue. +-type msghdr_flag() :: ctrunc | eor | errqueue | oob | trunc. -type msghdr_flags() :: [msghdr_flag()]. -type msghdr() :: #{ %% *Optional* target address %% *If* this field is specified for an unconnected %% socket, then it will be used as destination for the %% datagram. - target => sockaddr(), + addr => sockaddr(), - iov => [binary()], + iov => [binary()], - ctrl => cmsghdr(), + ctrl => [cmsghdr()], %% Only valid with recvmsg flags => msghdr_flags() }. +%% At some point we should be able to encode/decode the most common types +%% of control message headers. For now, we leave/take the data part raw +%% (as a binary) and leave it to the user to figure out (how to encode/decode +%% that bit). -type cmsghdr() :: #{ - level => protocol(), + level => protocol() | integer(), type => integer(), data => binary() }. @@ -1752,11 +1756,62 @@ do_recvfrom(SockRef, BufSz, EFlags, Timeout) -> %% --------------------------------------------------------------------------- %% -%% -spec recvmsg(Socket, Flags) -> {ok, MsgHdr} | {error, Reason} when -%% Socket :: socket(), -%% MsgHdr :: msghdr(), -%% Flags :: recv_flags(), -%% Reason :: term(). +recvmsg(Socket) -> + recvmsg(Socket, 0, 0, ?SOCKET_RECV_FLAGS_DEFAULT, ?SOCKET_RECV_TIMEOUT_DEFAULT). + +recvmsg(Socket, Flags) when is_list(Flags) -> + recvmsg(Socket, 0, 0, Flags, ?SOCKET_RECV_TIMEOUT_DEFAULT); +recvmsg(Socket, Timeout) -> + recvmsg(Socket, 0, 0, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout). + +-spec recvmsg(Socket, + BufSz, CtrlSz, + Flags, Timeout) -> {ok, MsgHdr} | {error, Reason} when + Socket :: socket(), + BufSz :: non_neg_integer(), + CtrlSz :: non_neg_integer(), + Flags :: recv_flags(), + Timeout :: timeout(), + MsgHdr :: msghdr(), + Reason :: term(). + +recvmsg(#socket{ref = SockRef}, BufSz, CtrlSz, Flags, Timeout) + when (is_integer(BufSz) andalso (BufSz >= 0)) andalso + (is_integer(CtrlSz) andalso (CtrlSz >= 0)) andalso + is_list(Flags) andalso + (is_integer(Timeout) orelse (Timeout =:= infinity)) -> + EFlags = enc_recv_flags(Flags), + do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Timeout). + +do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Timeout) -> + TS = timestamp(Timeout), + RecvRef = make_ref(), + case nif_recvmsg(SockRef, RecvRef, BufSz, CtrlSz, EFlags) of + {ok, _MsgHdr} = OK -> + OK; + + {error, eagain} -> + %% There is nothing just now, but we will be notified when there + %% is something to read (a select message). + NewTimeout = next_timeout(TS, Timeout), + receive + {select, SockRef, RecvRef, ready_input} -> + do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, + next_timeout(TS, Timeout)); + + {nif_abort, RecvRef, Reason} -> + {error, Reason} + + after NewTimeout -> + nif_cancel(SockRef, recvmsg, RecvRef), + flush_select_msgs(SockRef, RecvRef), + {error, timeout} + end; + + {error, _Reason} = ERROR -> + ERROR + + end. @@ -3171,6 +3226,9 @@ nif_recv(_SRef, _RecvRef, _Length, _Flags) -> nif_recvfrom(_SRef, _RecvRef, _Length, _Flags) -> erlang:error(badarg). +nif_recvmsg(_SRef, _RecvRef, _BufSz, _CtrlSz, _Flags) -> + erlang:error(badarg). + nif_cancel(_SRef, _Op, _Ref) -> erlang:error(badarg). -- cgit v1.2.3 From 90a150771faa3cf01e82919b0c17854de9987783 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 1 Aug 2018 19:42:32 +0200 Subject: [socket-nif] Processing of more cmsg headers Added processing or more cmsg headers (for more options). Now (also) supports: socket:timestamp. Also various fixes and cleanups. For some reason calling getopt(Sock, 0, {13, int}) (or similar) fails with badarg even though the nif-function (nif_getopt) actually returns a valid value (for instance: {ok, 0}). OTP-14831 --- erts/preloaded/src/socket.erl | 52 ++++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 13 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index b9d1705d45..5902c161db 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -90,12 +90,13 @@ raw_socket_option/0, timeval/0, - ip_tos_flag/0, + ip_tos/0, ip_mreq/0, ip_mreq_source/0, ip_pmtudisc/0, ip_msfilter_mode/0, ip_msfilter/0, + ip_pktinfo/0, ipv6_mreq/0, ipv6_pmtudisc/0, sctp_event_subscribe/0, @@ -107,6 +108,9 @@ msghdr_flag/0, msghdr_flags/0, msghdr/0, + cmsghdr_level/0, + cmsghdr_type/0, + cmsghdr_data/0, cmsghdr/0, uint8/0, @@ -168,11 +172,11 @@ usec := integer()}. %% If the integer value is used its up to the caller to ensure its valid! --type ip_tos_flag() :: lowdeley | - throughput | - reliability | - mincost | - integer(). +-type ip_tos() :: lowdeley | + throughput | + reliability | + mincost | + integer(). %% This type is used when requesting to become member of a multicast %% group with a call to setopt. Example: @@ -516,14 +520,32 @@ %% Only valid with recvmsg flags => msghdr_flags() }. -%% At some point we should be able to encode/decode the most common types -%% of control message headers. For now, we leave/take the data part raw -%% (as a binary) and leave it to the user to figure out (how to encode/decode -%% that bit). +%% We are able to (completely) decode *some* control message headers. +%% Even if we are able to decode both level and type, we may not be +%% able to decode the data, in which case it will be a binary. +-type ip_pktinfo() :: #{ + ifindex => non_neg_integer(), % Interface Index + spec_dst => ip4_address(), % Local Address + addr => ip4_address() % Header Destination address + }. +-type cmsghdr_level() :: socket | protocol() | integer(). +-type cmsghdr_type() :: timestamp | + rights | + credentials | + tos | + ttl | + origdstaddr | + integer(). +-type cmsghdr_data() :: timeval() | % if level = socket and type = timstamp + ip_pktinfo() | % if level = ip and type = pktinfo + ip_tos() | % if level = ip and type = tos + integer() | % if level = ip and type = ttl + sockaddr_in4() | % if level = ip and type = origdstaddr + binary(). -type cmsghdr() :: #{ - level => protocol() | integer(), - type => integer(), - data => binary() + level => cmsghdr_level(), + type => cmsghdr_type(), + data => cmsghdr_data() }. -define(SOCKET_DOMAIN_LOCAL, 1). @@ -1808,6 +1830,10 @@ do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Timeout) -> {error, timeout} end; + {error, closed} = ERROR -> + do_close(SockRef), + ERROR; + {error, _Reason} = ERROR -> ERROR -- cgit v1.2.3 From d8b1eace1cfe3184497752f345e5f6bc5def9769 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 3 Aug 2018 12:33:22 +0200 Subject: [socket-nif] Add preliminary support for sendmsg Added function sendmsg/2,3,4. Actually worked on the first try. Something must be wrong... Still no supported cmsghdr's (only support headers where the data part is already a binary, which therefor does not require any processing). So if the cmsghdrs actually work is unclear. OTP-14831 --- erts/preloaded/src/socket.erl | 172 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 157 insertions(+), 15 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 5902c161db..1459ee4869 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -38,12 +38,12 @@ send/2, send/3, send/4, sendto/3, sendto/4, sendto/5, - %% sendmsg/4, + sendmsg/2, sendmsg/3, sendmsg/4, %% writev/4, OR SENDV? It will be strange for recv then: recvv (instead of readv) recv/1, recv/2, recv/3, recv/4, recvfrom/1, recvfrom/2, recvfrom/3, recvfrom/4, - recvmsg/1, recvmsg/2, recvmsg/5, + recvmsg/1, recvmsg/2, recvmsg/3, recvmsg/5, %% readv/3, close/1, @@ -508,13 +508,15 @@ -type msghdr_flags() :: [msghdr_flag()]. -type msghdr() :: #{ %% *Optional* target address - %% *If* this field is specified for an unconnected - %% socket, then it will be used as destination for the - %% datagram. + %% Used on an unconnected socket to specify the + %% target address for a datagram. addr => sockaddr(), iov => [binary()], - + + %% The maximum size of the control buffer is platform + %% specific. It is the users responsibility to ensure + %% that its not exceeded. ctrl => [cmsghdr()], %% Only valid with recvmsg @@ -577,10 +579,12 @@ -define(SOCKET_SEND_FLAG_NOSIGNAL, 4). -define(SOCKET_SEND_FLAG_OOB, 5). --define(SOCKET_SEND_FLAGS_DEFAULT, []). --define(SOCKET_SEND_TIMEOUT_DEFAULT, infinity). --define(SOCKET_SENDTO_FLAGS_DEFAULT, []). --define(SOCKET_SENDTO_TIMEOUT_DEFAULT, ?SOCKET_SEND_TIMEOUT_DEFAULT). +-define(SOCKET_SEND_FLAGS_DEFAULT, []). +-define(SOCKET_SEND_TIMEOUT_DEFAULT, infinity). +-define(SOCKET_SENDTO_FLAGS_DEFAULT, []). +-define(SOCKET_SENDTO_TIMEOUT_DEFAULT, ?SOCKET_SEND_TIMEOUT_DEFAULT). +-define(SOCKET_SENDMSG_FLAGS_DEFAULT, []). +-define(SOCKET_SENDMSG_TIMEOUT_DEFAULT, ?SOCKET_SEND_TIMEOUT_DEFAULT). -define(SOCKET_RECV_FLAG_CMSG_CLOEXEC, 0). -define(SOCKET_RECV_FLAG_ERRQUEUE, 1). @@ -1390,14 +1394,123 @@ do_sendto(SockRef, Data, Dest, EFlags, Timeout) -> %% --------------------------------------------------------------------------- +%% +%% The only part of the msghdr() that *must* exist (a connected +%% socket need not specify the addr field) is the iov. +%% The ctrl field is optional, and the addr and flags are not +%% used when sending. +%% -%% -spec sendmsg(Socket, MsgHdr, Flags) -> ok | {error, Reason} when -%% Socket :: socket(), -%% MsgHdr :: msg_hdr(), -%% Flags :: send_flags(), -%% Reason :: term(). +-spec sendmsg(Socket, MsgHdr) -> ok | {error, Reason} when + Socket :: socket(), + MsgHdr :: msghdr(), + Reason :: term(). + +sendmsg(Socket, MsgHdr) -> + sendmsg(Socket, MsgHdr, + ?SOCKET_SENDMSG_FLAGS_DEFAULT, ?SOCKET_SENDMSG_TIMEOUT_DEFAULT). + + +-spec sendmsg(Socket, MsgHdr, Flags) -> ok | {error, Reason} when + Socket :: socket(), + MsgHdr :: msghdr(), + Flags :: send_flags(), + Reason :: term() + ; (Socket, MsgHdr, Timeout) -> ok | {error, Reason} when + Socket :: socket(), + MsgHdr :: msghdr(), + Timeout :: timeout(), + Reason :: term(). + +sendmsg(Socket, MsgHdr, Flags) when is_list(Flags) -> + sendmsg(Socket, MsgHdr, Flags, ?SOCKET_SENDMSG_TIMEOUT_DEFAULT); +sendmsg(Socket, MsgHdr, Timeout) + when is_integer(Timeout) orelse (Timeout =:= infinity) -> + sendmsg(Socket, MsgHdr, ?SOCKET_SENDMSG_FLAGS_DEFAULT, Timeout). + + +-spec sendmsg(Socket, MsgHdr, Flags, Timeout) -> ok | {error, Reason} when + Socket :: socket(), + MsgHdr :: msghdr(), + Flags :: send_flags(), + Timeout :: timeout(), + Reason :: term(). + +sendmsg(#socket{ref = SockRef}, #{iov := IOV} = MsgHdr, Flags, Timeout) + when is_list(IOV) andalso + is_list(Flags) andalso + (is_integer(Timeout) orelse (Timeout =:= infinity)) -> + try ensure_msghdr(MsgHdr) of + M -> + EFlags = enc_send_flags(Flags), + do_sendmsg(SockRef, M, EFlags, Timeout) + catch + throw:T -> + T; + error:Reason -> + {error, Reason} + end. + +do_sendmsg(SockRef, MsgHdr, EFlags, Timeout) -> + TS = timestamp(Timeout), + SendRef = make_ref(), + case nif_sendmsg(SockRef, SendRef, MsgHdr, EFlags) of + ok -> + %% We are done + ok; + + {error, eagain} -> + receive + {select, SockRef, SendRef, ready_output} -> + do_sendmsg(SockRef, MsgHdr, EFlags, + next_timeout(TS, Timeout)) + after Timeout -> + nif_cancel(SockRef, sendmsg, SendRef), + flush_select_msgs(SockRef, SendRef), + {error, timeout} + end; + + {error, _} = ERROR -> + ERROR + end. + +ensure_msghdr(#{iov := IOV} = M) when is_list(IOV) andalso (IOV =/= []) -> + M#{iov := erlang:iolist_to_iovec(IOV)}; +ensure_msghdr(_) -> + einval(). + + + +%% send(Sock, #{ctrl = Ctrl} = MsgHdr, Flags) when is_list(Ctrl) -> +%% case encode_cmsghdrs(Ctrl) of +%% undefined -> +%% send(Sock, maps:remove(ctrl, MsgHdr), Flags); +%% Ctrl2 -> +%% send(Sock, MsgHdr#{ctrl = Ctrl2}, Flags) +%% end. + +%% encode_cmsghdrs([]) -> +%% undefined; +%% encode_cmsghdrs(Hdrs) -> +%% encode_cmsghdrs(Hdrs, []). + +%% encode_cmsghdrs([], Acc) -> +%% list_to_binary(lists:reverse(Acc)); +%% encode_cmsghdrs([H|T], Acc) when is_binary(H) -> +%% encode_cmsghdrs(T, [H|Acc]); +%% encode_cmsghdrs([#{level := Level, +%% type := Type, +%% data := Data} | T], Acc) -> +%% case nif_encode_cmsghdr(Level, Type, Data) of +%% {ok, Bin} when is_binary(Bin) -> +%% encode_cmsghdrs(T, [Bin | Acc]); +%% {error, _} = ERROR -> +%% ERROR +%% end. + + %% =========================================================================== %% @@ -1778,14 +1891,40 @@ do_recvfrom(SockRef, BufSz, EFlags, Timeout) -> %% --------------------------------------------------------------------------- %% +-spec recvmsg(Socket) -> {ok, MsgHdr} | {error, Reason} when + Socket :: socket(), + MsgHdr :: msghdr(), + Reason :: term(). + recvmsg(Socket) -> recvmsg(Socket, 0, 0, ?SOCKET_RECV_FLAGS_DEFAULT, ?SOCKET_RECV_TIMEOUT_DEFAULT). +-spec recvmsg(Socket, Flags) -> {ok, MsgHdr} | {error, Reason} when + Socket :: socket(), + Flags :: recv_flags(), + MsgHdr :: msghdr(), + Reason :: term() + ; (Socket, Timeout) -> {ok, MsgHdr} | {error, Reason} when + Socket :: socket(), + Timeout :: timeout(), + MsgHdr :: msghdr(), + Reason :: term(). + recvmsg(Socket, Flags) when is_list(Flags) -> recvmsg(Socket, 0, 0, Flags, ?SOCKET_RECV_TIMEOUT_DEFAULT); recvmsg(Socket, Timeout) -> recvmsg(Socket, 0, 0, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout). +-spec recvmsg(Socket, Flags, Timeout) -> {ok, MsgHdr} | {error, Reason} when + Socket :: socket(), + Flags :: recv_flags(), + Timeout :: timeout(), + MsgHdr :: msghdr(), + Reason :: term(). + +recvmsg(Socket, Flags, Timeout) -> + recvmsg(Socket, 0, 0, Flags, Timeout). + -spec recvmsg(Socket, BufSz, CtrlSz, Flags, Timeout) -> {ok, MsgHdr} | {error, Reason} when @@ -3246,6 +3385,9 @@ nif_send(_SockRef, _SendRef, _Data, _Flags) -> nif_sendto(_SRef, _SendRef, _Data, _Dest, _Flags) -> erlang:error(badarg). +nif_sendmsg(_SRef, _SendRef, _MsgHdr, _Flags) -> + erlang:error(badarg). + nif_recv(_SRef, _RecvRef, _Length, _Flags) -> erlang:error(badarg). -- cgit v1.2.3 From 01601a4db44b3adccfbcc07129a4584649252736 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 3 Aug 2018 15:08:30 +0200 Subject: [socket-nif] Add more data types (with doc) and (C) debug Add more debug printouts for the new sendmsg. Also added new (erlang) types with doc. OTP-14831 --- erts/preloaded/src/socket.erl | 64 ++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 22 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 1459ee4869..3d65f52a2b 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -99,6 +99,8 @@ ip_pktinfo/0, ipv6_mreq/0, ipv6_pmtudisc/0, + sctp_assoc_id/0, + sctp_sndrcvinfo/0, sctp_event_subscribe/0, sctp_assocparams/0, sctp_initmsg/0, @@ -116,7 +118,8 @@ uint8/0, uint16/0, uint20/0, - uint32/0 + uint32/0, + int32/0 ]). @@ -124,6 +127,7 @@ -type uint16() :: 0..16#FFFF. -type uint20() :: 0..16#FFFFF. -type uint32() :: 0..16#FFFFFFFF. +-type int32() :: -2147483648..2147483647. %% We support only a subset of all domains. @@ -171,6 +175,12 @@ -type timeval() :: #{sec := integer(), usec := integer()}. +-type ip_pktinfo() :: #{ + ifindex := non_neg_integer(), % Interface Index + spec_dst := ip4_address(), % Local Address + addr := ip4_address() % Header Destination address + }. + %% If the integer value is used its up to the caller to ensure its valid! -type ip_tos() :: lowdeley | throughput | @@ -208,16 +218,29 @@ %% slist: List of source addresses -type ip_msfilter_mode() :: include | exclude. --type ip_msfilter() :: #{multiaddr => ip4_address(), - interface => ip4_address(), - mode => ip_msfilter_mode(), - slist => [ip4_address()]}. +-type ip_msfilter() :: #{multiaddr := ip4_address(), + interface := ip4_address(), + mode := ip_msfilter_mode(), + slist := [ip4_address()]}. -type ipv6_mreq() :: #{multiaddr := ip6_address(), interface := non_neg_integer()}. -type ipv6_pmtudisc() :: ip_pmtudisc(). +-type sctp_assoc_id() :: int32(). +-type sctp_sndrcvinfo() :: #{ + stream := uint16(), + ssn := uint16(), + flags := uint16(), + ppid := uint16(), + context := uint16(), + timetolive := uint16(), + tsn := uint16(), + cumtsn := uint16(), + assoc_id := sctp_assoc_id() + }. + -type sctp_event_subscribe() :: #{data_in := boolean(), association := boolean(), address := boolean(), @@ -229,7 +252,7 @@ authentication := boolean(), sender_dry := boolean()}. --type sctp_assocparams() :: #{assoc_id := integer(), +-type sctp_assocparams() :: #{assoc_id := sctp_assoc_id(), max_rxt := uint16(), num_peer_dests := uint16(), peer_rwnd := uint32(), @@ -242,7 +265,7 @@ max_init_timeo := uint16() }. --type sctp_rtoinfo() :: #{assoc_id := integer(), +-type sctp_rtoinfo() :: #{assoc_id := sctp_assoc_id(), initial := uint32(), max := uint32(), min := uint32()}. @@ -510,32 +533,29 @@ %% *Optional* target address %% Used on an unconnected socket to specify the %% target address for a datagram. - addr => sockaddr(), + addr := sockaddr(), - iov => [binary()], + iov := [binary()], %% The maximum size of the control buffer is platform %% specific. It is the users responsibility to ensure %% that its not exceeded. - ctrl => [cmsghdr()], + ctrl := [cmsghdr()], %% Only valid with recvmsg - flags => msghdr_flags() + flags := msghdr_flags() }. %% We are able to (completely) decode *some* control message headers. %% Even if we are able to decode both level and type, we may not be %% able to decode the data, in which case it will be a binary. --type ip_pktinfo() :: #{ - ifindex => non_neg_integer(), % Interface Index - spec_dst => ip4_address(), % Local Address - addr => ip4_address() % Header Destination address - }. + -type cmsghdr_level() :: socket | protocol() | integer(). -type cmsghdr_type() :: timestamp | - rights | - credentials | + pktinfo | tos | ttl | + rights | + credentials | origdstaddr | integer(). -type cmsghdr_data() :: timeval() | % if level = socket and type = timstamp @@ -545,9 +565,9 @@ sockaddr_in4() | % if level = ip and type = origdstaddr binary(). -type cmsghdr() :: #{ - level => cmsghdr_level(), - type => cmsghdr_type(), - data => cmsghdr_data() + level := cmsghdr_level(), + type := cmsghdr_type(), + data := cmsghdr_data() }. -define(SOCKET_DOMAIN_LOCAL, 1). @@ -1476,6 +1496,7 @@ do_sendmsg(SockRef, MsgHdr, EFlags, Timeout) -> ensure_msghdr(#{iov := IOV} = M) when is_list(IOV) andalso (IOV =/= []) -> M#{iov := erlang:iolist_to_iovec(IOV)}; + %% M; ensure_msghdr(_) -> einval(). @@ -1887,7 +1908,6 @@ do_recvfrom(SockRef, BufSz, EFlags, Timeout) -> end. - %% --------------------------------------------------------------------------- %% -- cgit v1.2.3 From ee2eadd1c61d4237ee4044260665c82edf559228 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 3 Aug 2018 18:55:30 +0200 Subject: [socket-nif] Add support for (recvmsg) control message ipv6_pktinfo Added support for (recvmsg) control message ipv6_pktinfo, for level = ipv6 and type = pktinfo. This is enabled by setting the socket option: recvpktinfo for level ipv6. Not yet tested! OTP-14831 --- erts/preloaded/src/socket.erl | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 3d65f52a2b..8af052e149 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -228,6 +228,12 @@ -type ipv6_pmtudisc() :: ip_pmtudisc(). +-type ipv6_pktinfo() :: #{ + addr := ip6_address(), + ifindex := integer() + }. + + -type sctp_assoc_id() :: int32(). -type sctp_sndrcvinfo() :: #{ stream := uint16(), @@ -560,6 +566,7 @@ integer(). -type cmsghdr_data() :: timeval() | % if level = socket and type = timstamp ip_pktinfo() | % if level = ip and type = pktinfo + ipv6_pktinfo() | % if level = ipv6 and type = pktinfo ip_tos() | % if level = ip and type = tos integer() | % if level = ip and type = ttl sockaddr_in4() | % if level = ip and type = origdstaddr -- cgit v1.2.3 From 929ae46220f402d6f36072c46fe27ba39ad48d1b Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 13 Sep 2018 15:44:21 +0200 Subject: [socket-nif] CMsgHdr and various doc related changes Updated the (send) cmsghdr type and the handling of it (in the nif code). Still not tested! Removed the is_loaded nif function. Tried to get fix the doc build problem (socket.erl *i think*), which causes socket.html generation to fail with: "cannot find module exporting type" To solve this I tried to run dialyzer on preloaded, and ran into problems with enc_setopt_value. Update various specs and types to "solve" this (which did not work). Updated the nif-stub functions to make dialyzer happy. --- erts/preloaded/src/Makefile | 29 ++++- erts/preloaded/src/net.erl | 42 +++---- erts/preloaded/src/socket.erl | 263 ++++++++++++++++++++++++++---------------- 3 files changed, 208 insertions(+), 126 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile index fae60a4491..2a80837719 100644 --- a/erts/preloaded/src/Makefile +++ b/erts/preloaded/src/Makefile @@ -78,6 +78,10 @@ STDLIB_INCLUDE=$(ERL_TOP)/lib/stdlib/include ERL_COMPILE_FLAGS += +debug_info -I$(KERNEL_SRC) -I$(KERNEL_INCLUDE) +DIA_PLT = erts-preloaded.plt +DIA_ANALYSIS = $(basename $(DIA_PLT)).dialyzer_analysis + + debug opt: $(TARGET_FILES) clean: @@ -89,7 +93,6 @@ copy: $(APP_TARGET): $(APP_SRC) $(ERL_TOP)/erts/vsn.mk $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@ - include $(ERL_TOP)/make/otp_release_targets.mk release_spec: $(APP_TARGET) @@ -104,6 +107,30 @@ release_docs_spec: list_preloaded: @echo $(PRE_LOADED_MODULES) +dclean: + rm -f $(DIA_PLT) + rm -f $(DIA_ANALYSIS) + +dialyzer_plt: $(DIA_PLT) + +$(DIA_PLT): $(ERL_FILES) + @echo "Building ($(basename $(DIA_PLT))) plt file" + @dialyzer --build_plt \ + --output_plt $@ \ + -r ../ebin \ + ../../../lib/kernel/ebin \ + ../../../lib/stdlib/ebin \ + ../../../lib/crypto/ebin \ + ../../../lib/compiler/ebin \ + --output $(DIA_ANALYSIS) \ + --verbose + +dialyzer: $(DIA_PLT) + @echo "Running dialyzer on $(basename $(DIA_PLT))" + @dialyzer --plt $< \ + ../ebin \ + --verbose + # # Combine a BEAM assembly script file a stub Erlang file into a BEAM file. # See add_abstract_chunk script. diff --git a/erts/preloaded/src/net.erl b/erts/preloaded/src/net.erl index 01b12696e9..0183a69ddb 100644 --- a/erts/preloaded/src/net.erl +++ b/erts/preloaded/src/net.erl @@ -20,9 +20,11 @@ -module(net). +-compile(no_native). + %% Administrative and "global" utility functions -export([ - on_load/0, on_load/1, on_load/2, + on_load/0, on_load/1, info/0, command/1 ]). @@ -105,23 +107,10 @@ on_load() -> on_load(#{}). -spec on_load(Extra) -> ok when - Extra :: maps:map(). - -on_load(Extra) when is_map(Extra) -> - on_load(atom_to_list(?MODULE), Extra). - --spec on_load(Path, Extra) -> ok when - Path :: string(), - Extra :: maps:map(). - -on_load(Path, Extra) when is_list(Path) andalso is_map(Extra) -> - on_load(nif_is_loaded(), Path, Extra). - -on_load(true, _Path, _Extra) -> - ok; -on_load(false, Path, Extra) -> - ok = erlang:load_nif(Path, Extra). + Extra :: map(). +on_load(Extra) -> + ok = erlang:load_nif(atom_to_list(?MODULE), Extra). -spec info() -> list(). @@ -315,29 +304,26 @@ if_names() -> %% %% =========================================================================== -nif_is_loaded() -> - false. - nif_info() -> - erlang:error(badarg). + erlang:nif_error(undef). nif_command(_Cmd) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_gethostname() -> - erlang:error(badarg). + erlang:nif_error(undef). nif_getnameinfo(_Addr, _Flags) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_getaddrinfo(_Host, _Service, _Hints) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_if_name2index(_Name) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_if_index2name(_Id) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_if_names() -> - erlang:error(badarg). + erlang:nif_error(undef). diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 8af052e149..d1053d88f9 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -20,11 +20,12 @@ -module(socket). +-compile(no_native). -compile({no_auto_import,[error/1]}). %% Administrative and "global" utility functions -export([ - on_load/0, on_load/1, on_load/2, + on_load/0, on_load/1, info/0, ensure_sockaddr/1 ]). @@ -112,8 +113,8 @@ msghdr/0, cmsghdr_level/0, cmsghdr_type/0, - cmsghdr_data/0, - cmsghdr/0, + %% cmsghdr_data/0, + cmsghdr_recv/0, cmsghdr_send/0, uint8/0, uint16/0, @@ -311,7 +312,7 @@ %% Int - Raw level, sent down and used "as is". -type sockopt_level() :: otp | socket | - ip | ipv6 | tcp | udp | sctp | raw | + ip | ipv6 | tcp | udp | sctp | non_neg_integer(). %% There are some options that are 'read-only'. @@ -546,7 +547,7 @@ %% The maximum size of the control buffer is platform %% specific. It is the users responsibility to ensure %% that its not exceeded. - ctrl := [cmsghdr()], + ctrl := [cmsghdr_recv()] | [cmsghdr_send()], %% Only valid with recvmsg flags := msghdr_flags() @@ -555,7 +556,7 @@ %% Even if we are able to decode both level and type, we may not be %% able to decode the data, in which case it will be a binary. --type cmsghdr_level() :: socket | protocol() | integer(). +-type cmsghdr_level() :: socket | ip | ipv6 | integer(). -type cmsghdr_type() :: timestamp | pktinfo | tos | @@ -564,18 +565,44 @@ credentials | origdstaddr | integer(). --type cmsghdr_data() :: timeval() | % if level = socket and type = timstamp - ip_pktinfo() | % if level = ip and type = pktinfo - ipv6_pktinfo() | % if level = ipv6 and type = pktinfo - ip_tos() | % if level = ip and type = tos - integer() | % if level = ip and type = ttl - sockaddr_in4() | % if level = ip and type = origdstaddr - binary(). --type cmsghdr() :: #{ - level := cmsghdr_level(), - type := cmsghdr_type(), - data := cmsghdr_data() - }. +%% Do we need this? See cmsghdr() +%% -type cmsghdr_data() :: timeval() | % if level = socket and type = timstamp +%% ip_pktinfo() | % if level = ip and type = pktinfo +%% ipv6_pktinfo() | % if level = ipv6 and type = pktinfo +%% ip_tos() | % if level = ip and type = tos +%% integer() | % if level = ip and type = ttl +%% sockaddr_in4() | % if level = ip and type = origdstaddr +%% binary(). +%% -type cmsghdr() :: #{ +%% level := cmsghdr_level(), +%% type := cmsghdr_type(), +%% data := cmsghdr_data() +%% }. + +-type cmsghdr_recv() :: + #{level := socket, type := timestamp, data := timeval()} | + #{level := socket, type := rights, data := binary()} | + #{level := socket, type := credentials, data := binary()} | + #{level := socket, type := integer(), data := binary()} | + #{level := ip, type := tos, data := ip_tos()} | + #{level := ip, type := ttl, data := integer()} | + #{level := ip, type := pktinfo, data := ip_pktinfo()} | + #{level := ip, type := origdstaddr, data := sockaddr_in4()} | + #{level := ip, type := integer(), data := binary()} | + #{level := ipv6, type := pktinfo, data := ipv6_pktinfo()} | + #{level := ipv6, type := integer(), data := binary()} | + #{level := integer(), type := integer(), data := binary()}. + + +-type cmsghdr_send() :: + #{level := socket, type := integer(), data := binary()} | + #{level := ip, type := tos, data := ip_tos() | binary()} | + #{level := ip, type := ttl, data := integer() | binary()} | + #{level := ip, type := integer(), data := binary()} | + #{level := ipv6, type := integer(), data := binary()} | + #{level := udp, type := integer(), data := binary()} | + #{level := integer(), type := integer(), data := binary()}. + -define(SOCKET_DOMAIN_LOCAL, 1). -define(SOCKET_DOMAIN_UNIX, ?SOCKET_DOMAIN_LOCAL). @@ -630,10 +657,12 @@ -define(SOCKET_OPT_LEVEL_UDP, 5). -define(SOCKET_OPT_LEVEL_SCTP, 6). +%% *** OTP (socket) options -define(SOCKET_OPT_OTP_DEBUG, 1). -define(SOCKET_OPT_OTP_IOW, 2). -define(SOCKET_OPT_OTP_CTRL_PROC, 3). +%% *** SOCKET (socket) options -define(SOCKET_OPT_SOCK_ACCEPTCONN, 1). %% -define(SOCKET_OPT_SOCK_ACCEPTFILTER, 2). % FreeBSD -define(SOCKET_OPT_SOCK_BINDTODEVICE, 3). @@ -667,6 +696,7 @@ -define(SOCKET_OPT_SOCK_TIMESTAMP, 31). -define(SOCKET_OPT_SOCK_TYPE, 32). +%% *** IP (socket) options -define(SOCKET_OPT_IP_ADD_MEMBERSHIP, 1). -define(SOCKET_OPT_IP_ADD_SOURCE_MEMBERSHIP, 2). -define(SOCKET_OPT_IP_BLOCK_SOURCE, 3). @@ -701,6 +731,7 @@ -define(SOCKET_OPT_IP_TTL, 32). -define(SOCKET_OPT_IP_UNBLOCK_SOURCE, 33). +%% *** IPv6 (socket) options -define(SOCKET_OPT_IPV6_ADDRFORM, 1). -define(SOCKET_OPT_IPV6_ADD_MEMBERSHIP, 2). -define(SOCKET_OPT_IPV6_AUTHHDR, 3). % Obsolete? @@ -734,6 +765,7 @@ %% -define(SOCKET_OPT_IPV6_USE_MIN_MTU, 31). % FreeBSD -define(SOCKET_OPT_IPV6_V6ONLY, 32). +%% *** TCP (socket) options -define(SOCKET_OPT_TCP_CONGESTION, 1). -define(SOCKET_OPT_TCP_CORK, 2). %% -define(SOCKET_OPT_TCP_INFO, 3). @@ -748,8 +780,10 @@ %% -define(SOCKET_OPT_TCP_SYNCNT, 12). %% -define(SOCKET_OPT_TCP_USER_TIMEOUT, 13). +%% *** UDP (socket) options -define(SOCKET_OPT_UDP_CORK, 1). +%% *** SCTP (socket) options %% -define(SOCKET_OPT_SCTP_ADAPTION_LAYER, 1). -define(SOCKET_OPT_SCTP_ASSOCINFO, 2). %% -define(SOCKET_OPT_SCTP_AUTH_ACTIVE_KEY, 3). @@ -802,23 +836,10 @@ on_load() -> on_load(#{}). -spec on_load(Extra) -> ok when - Extra :: maps:map(). - -on_load(Extra) when is_map(Extra) -> - on_load(atom_to_list(?MODULE), Extra). - --spec on_load(Path, Extra) -> ok when - Path :: string(), - Extra :: maps:map(). + Extra :: map(). -on_load(Path, Extra) when is_list(Path) andalso is_map(Extra) -> - on_load(nif_is_loaded(), Path, Extra). - -on_load(true, _Path, _Extra) -> - ok; -on_load(false, Path, Extra) -> - %% ok = erlang:load_nif(Path, maps:put(timestamp, formated_timestamp(), Extra)). - ok = erlang:load_nif(Path, Extra). +on_load(Extra) -> + ok = erlang:load_nif(atom_to_list(?MODULE), Extra). @@ -1022,8 +1043,8 @@ bind(#socket{ref = SockRef}, Addrs, Action) when is_list(Addrs) andalso ((Action =:= add) orelse (Action =:= remove)) -> try begin - ensure_type(seqpacket, which_type(SockRef)), - ensure_proto(sctp, which_protocol(SockRef)), + ensure_type(SockRef, seqpacket), + ensure_proto(SockRef, sctp), validate_addrs(which_domain(SockRef), Addrs), nif_bind(SockRef, Addrs, Action) end @@ -1508,38 +1529,6 @@ ensure_msghdr(_) -> einval(). - -%% send(Sock, #{ctrl = Ctrl} = MsgHdr, Flags) when is_list(Ctrl) -> -%% case encode_cmsghdrs(Ctrl) of -%% undefined -> -%% send(Sock, maps:remove(ctrl, MsgHdr), Flags); -%% Ctrl2 -> -%% send(Sock, MsgHdr#{ctrl = Ctrl2}, Flags) -%% end. - -%% encode_cmsghdrs([]) -> -%% undefined; -%% encode_cmsghdrs(Hdrs) -> -%% encode_cmsghdrs(Hdrs, []). - -%% encode_cmsghdrs([], Acc) -> -%% list_to_binary(lists:reverse(Acc)); -%% encode_cmsghdrs([H|T], Acc) when is_binary(H) -> -%% encode_cmsghdrs(T, [H|Acc]); -%% encode_cmsghdrs([#{level := Level, -%% type := Type, -%% data := Data} | T], Acc) -> -%% case nif_encode_cmsghdr(Level, Type, Data) of -%% {ok, Bin} when is_binary(Bin) -> -%% encode_cmsghdrs(T, [Bin | Acc]); -%% {error, _} = ERROR -> -%% ERROR -%% end. - - - - - %% =========================================================================== %% %% writev - write data into multiple buffers @@ -1924,7 +1913,8 @@ do_recvfrom(SockRef, BufSz, EFlags, Timeout) -> Reason :: term(). recvmsg(Socket) -> - recvmsg(Socket, 0, 0, ?SOCKET_RECV_FLAGS_DEFAULT, ?SOCKET_RECV_TIMEOUT_DEFAULT). + recvmsg(Socket, 0, 0, + ?SOCKET_RECV_FLAGS_DEFAULT, ?SOCKET_RECV_TIMEOUT_DEFAULT). -spec recvmsg(Socket, Flags) -> {ok, MsgHdr} | {error, Reason} when Socket :: socket(), @@ -2118,6 +2108,12 @@ shutdown(#socket{ref = SockRef}, How) -> ; (Socket, sctp, sctp_socket_option(), Value) -> ok | {error, Reason} when Socket :: socket(), Value :: term(), + Reason :: term() + ; (Socket, Level, Key, Value) -> ok | {error, Reason} when + Socket :: socket(), + Level :: non_neg_integer(), + Key :: non_neg_integer(), + Value :: binary(), Reason :: term(). setopt(#socket{ref = SockRef}, Level, Key, Value) -> @@ -2223,6 +2219,11 @@ getopt(#socket{ref = SockRef}, Level, Key) -> %% These are internal "shortcut" functions for the options %% domain, type and protocol. + +-spec which_domain(SockRef) -> Domain when + SockRef :: reference(), + Domain :: domain(). + which_domain(SockRef) -> case nif_getopt(SockRef, true, ?SOCKET_OPT_LEVEL_SOCKET, ?SOCKET_OPT_SOCK_DOMAIN) of @@ -2233,6 +2234,10 @@ which_domain(SockRef) -> end. +-spec which_type(SockRef) -> Type when + SockRef :: reference(), + Type :: type(). + which_type(SockRef) -> case nif_getopt(SockRef, true, ?SOCKET_OPT_LEVEL_SOCKET, ?SOCKET_OPT_SOCK_TYPE) of @@ -2242,6 +2247,10 @@ which_type(SockRef) -> throw(ERROR) end. +-spec which_protocol(SockRef) -> Protocol when + SockRef :: reference(), + Protocol :: protocol(). + which_protocol(SockRef) -> case nif_getopt(SockRef, true, ?SOCKET_OPT_LEVEL_SOCKET, ?SOCKET_OPT_SOCK_PROTOCOL) of @@ -2409,6 +2418,57 @@ enc_setopt_key(Level, Opt, Domain, Type, Protocol) -> %% encode the value into an more "manageable" type. %% It also handles "aliases" (see linger). +-spec enc_setopt_value(otp, otp_socket_option(), + Value, Domain, Type, Protocol) -> term() when + Value :: term(), + Domain :: domain(), + Type :: type(), + Protocol :: protocol() + ; (socket, socket_option(), + Value, Domain, Type, Protocol) -> term() when + Value :: term(), + Domain :: domain(), + Type :: type(), + Protocol :: protocol() + ; (ip, ip_socket_option(), + Value, Domain, Type, Protocol) -> term() when + Value :: term(), + Domain :: domain(), + Type :: type(), + Protocol :: protocol() + ; (ipv6, ipv6_socket_option(), + Value, Domain, Type, Protocol) -> term() when + Value :: term(), + Domain :: domain(), + Type :: type(), + Protocol :: protocol() + ; (tcp, tcp_socket_option(), + Value, Domain, Type, Protocol) -> term() when + Value :: term(), + Domain :: domain(), + Type :: type(), + Protocol :: protocol() + ; (udp, udp_socket_option(), + Value, Domain, Type, Protocol) -> term() when + Value :: term(), + Domain :: domain(), + Type :: type(), + Protocol :: protocol() + ; (sctp, sctp_socket_option(), + Value, Domain, Type, Protocol) -> term() when + Value :: term(), + Domain :: domain(), + Type :: type(), + Protocol :: protocol() + ; (Level, Opt, + Value, Domain, Type, Protocol) -> term() when + Level :: integer(), + Opt :: integer(), + Value :: binary(), + Domain :: domain(), + Type :: type(), + Protocol :: protocol(). + enc_setopt_value(otp, debug, V, _, _, _) when is_boolean(V) -> V; enc_setopt_value(otp, iow, V, _, _, _) when is_boolean(V) -> @@ -2762,8 +2822,8 @@ enc_setopt_value(sctp, rtoinfo, #{assoc_id := AssocId, enc_setopt_value(sctp = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); -enc_setopt_value(raw = L, Opt, _V, _D, _T, _P) -> - not_supported({L, Opt}); +%% enc_setopt_value(raw = L, Opt, _V, _D, _T, _P) -> +%% not_supported({L, Opt}); %% Is this correct? What about getopt? enc_setopt_value(L, Opt, V, _, _, _) @@ -2788,10 +2848,10 @@ enc_getopt_key(Level, Opt, Domain, Type, Protocol) -> %% +++ Decode getopt value +++ %% %% For the most part, we simply let the value pass through, but for some -%% values we do an actual decode. +%% values we may need to do an actual decode. %% -%% Let the user deal with this... +%% Let the user deal with this for now... dec_getopt_value(_L, _Opt, V, _D, _T, _P) -> V. @@ -3361,15 +3421,26 @@ tdiff(T1, T2) -> %% %% =========================================================================== +-spec not_supported(What) -> no_return() when + What :: term(). + not_supported(What) -> error({not_supported, What}). +-spec unknown(What) -> no_return() when + What :: term(). + unknown(What) -> error({unknown, What}). +-spec einval() -> no_return(). + einval() -> error(einval). +-spec error(Reason) -> no_return() when + Reason :: term(). + error(Reason) -> throw({error, Reason}). @@ -3380,71 +3451,69 @@ error(Reason) -> %% %% =========================================================================== -nif_is_loaded() -> false. - nif_info() -> - erlang:error(badarg). + erlang:nif_error(undef). nif_open(_Domain, _Type, _Protocol, _Extra) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_bind(_SRef, _SockAddr) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_bind(_SRef, _SockAddrs, _Action) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_connect(_SRef, _SockAddr) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_finalize_connection(_SRef) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_listen(_SRef, _Backlog) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_accept(_SRef, _Ref) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_send(_SockRef, _SendRef, _Data, _Flags) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_sendto(_SRef, _SendRef, _Data, _Dest, _Flags) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_sendmsg(_SRef, _SendRef, _MsgHdr, _Flags) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_recv(_SRef, _RecvRef, _Length, _Flags) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_recvfrom(_SRef, _RecvRef, _Length, _Flags) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_recvmsg(_SRef, _RecvRef, _BufSz, _CtrlSz, _Flags) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_cancel(_SRef, _Op, _Ref) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_close(_SRef) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_shutdown(_SRef, _How) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_finalize_close(_SRef) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_setopt(_Ref, _IsEnc, _Lev, _Key, _Val) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_getopt(_Ref, _IsEnc, _Lev, _Key) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_sockname(_Ref) -> - erlang:error(badarg). + erlang:nif_error(undef). nif_peername(_Ref) -> - erlang:error(badarg). + erlang:nif_error(undef). -- cgit v1.2.3 From d72a3c72dc6e74fb06e4e488db32fc819ce0c088 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 18 Sep 2018 12:40:43 +0200 Subject: [socket-nif] Cleanup and guards Some cleanup and also added a guard to sendmsg to ensure that only ctrl (cmsg hdr) with actual content will be "sent down". OTP-14831 --- erts/preloaded/src/socket.erl | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index d1053d88f9..ad7a35694b 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -565,20 +565,6 @@ credentials | origdstaddr | integer(). -%% Do we need this? See cmsghdr() -%% -type cmsghdr_data() :: timeval() | % if level = socket and type = timstamp -%% ip_pktinfo() | % if level = ip and type = pktinfo -%% ipv6_pktinfo() | % if level = ipv6 and type = pktinfo -%% ip_tos() | % if level = ip and type = tos -%% integer() | % if level = ip and type = ttl -%% sockaddr_in4() | % if level = ip and type = origdstaddr -%% binary(). -%% -type cmsghdr() :: #{ -%% level := cmsghdr_level(), -%% type := cmsghdr_type(), -%% data := cmsghdr_data() -%% }. - -type cmsghdr_recv() :: #{level := socket, type := timestamp, data := timeval()} | #{level := socket, type := rights, data := binary()} | @@ -592,8 +578,6 @@ #{level := ipv6, type := pktinfo, data := ipv6_pktinfo()} | #{level := ipv6, type := integer(), data := binary()} | #{level := integer(), type := integer(), data := binary()}. - - -type cmsghdr_send() :: #{level := socket, type := integer(), data := binary()} | #{level := ip, type := tos, data := ip_tos() | binary()} | @@ -1522,9 +1506,10 @@ do_sendmsg(SockRef, MsgHdr, EFlags, Timeout) -> ERROR end. +ensure_msghdr(#{ctrl := []} = M) -> + ensure_msghdr(maps:remove(ctrl, M)); ensure_msghdr(#{iov := IOV} = M) when is_list(IOV) andalso (IOV =/= []) -> M#{iov := erlang:iolist_to_iovec(IOV)}; - %% M; ensure_msghdr(_) -> einval(). -- cgit v1.2.3 From e01a856c993b55c3fbc76fd429783d4aad5bfc80 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 19 Sep 2018 18:21:47 +0200 Subject: [socket-nif] Add proper connect and accept timeout handling Added proper connect and accept timeout handling. Made use of the enif_select(mode = cancel) feature. Each time a timeout expires, the previous operation (connect or accept) has to be cancelled (actually its the select operation that has to be cancelled). Only partial implementation of cancel for now (connect and accept). More to follow... OTP-14831 --- erts/preloaded/src/socket.erl | 111 ++++++++++-------------------------------- 1 file changed, 26 insertions(+), 85 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index ad7a35694b..1c16c94711 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -1137,7 +1137,7 @@ connect(#socket{ref = SockRef}, #{family := Fam} = SockAddr, Timeout) %% nif_finalize_connection(SockRef) after NewTimeout -> - nif_cancel(SockRef, connect, Ref), + cancel(SockRef, connect, Ref), {error, timeout} end; {error, _} = ERROR -> @@ -1145,7 +1145,6 @@ connect(#socket{ref = SockRef}, #{family := Fam} = SockAddr, Timeout) end. - %% =========================================================================== %% %% listen - listen for connections on a socket @@ -1227,13 +1226,12 @@ do_accept(LSockRef, Timeout) -> {error, Reason} after NewTimeout -> - nif_cancel(LSockRef, accept, AccRef), - flush_select_msgs(LSockRef, AccRef), + cancel(LSockRef, accept, AccRef), {error, timeout} end; {error, _} = ERROR -> - nif_cancel(LSockRef, accept, AccRef), % Just to be on the safe side... + cancel(LSockRef, accept, AccRef), % Just to be on the safe side... ERROR end. @@ -1305,8 +1303,7 @@ do_send(SockRef, Data, EFlags, Timeout) -> {error, Reason} after NewTimeout -> - nif_cancel(SockRef, send, SendRef), - flush_select_msgs(SockRef, SendRef), + cancel(SockRef, send, SendRef), {error, {timeout, size(Data)}} end; {error, eagain} -> @@ -1319,8 +1316,7 @@ do_send(SockRef, Data, EFlags, Timeout) -> {error, Reason} after Timeout -> - nif_cancel(SockRef, send, SendRef), - flush_select_msgs(SockRef, SendRef), + cancel(SockRef, send, SendRef), {error, {timeout, size(Data)}} end; @@ -1403,8 +1399,7 @@ do_sendto(SockRef, Data, Dest, EFlags, Timeout) -> {error, Reason} after Timeout -> - nif_cancel(SockRef, sendto, SendRef), - flush_select_msgs(SockRef, SendRef), + cancel(SockRef, sendto, SendRef), {error, timeout} end; @@ -1414,8 +1409,7 @@ do_sendto(SockRef, Data, Dest, EFlags, Timeout) -> do_sendto(SockRef, Data, Dest, EFlags, next_timeout(TS, Timeout)) after Timeout -> - nif_cancel(SockRef, sendto, SendRef), - flush_select_msgs(SockRef, SendRef), + cancel(SockRef, sendto, SendRef), {error, timeout} end; @@ -1497,8 +1491,7 @@ do_sendmsg(SockRef, MsgHdr, EFlags, Timeout) -> do_sendmsg(SockRef, MsgHdr, EFlags, next_timeout(TS, Timeout)) after Timeout -> - nif_cancel(SockRef, sendmsg, SendRef), - flush_select_msgs(SockRef, SendRef), + cancel(SockRef, sendmsg, SendRef), {error, timeout} end; @@ -1519,62 +1512,6 @@ ensure_msghdr(_) -> %% writev - write data into multiple buffers %% -%% send(Socket, Data, Flags, Timeout) -%% when (is_list(Data) orelse is_binary(Data)) andalso is_list(Flags) -> -%% IOVec = erlang:iolist_to_iovec(Data), -%% EFlags = enc_send_flags(Flags), -%% send_iovec(Socket, IOVec, EFlags, Timeout). - - -%% %% Iterate over the IO-vector (list of binaries). - -%% send_iovec(_Socket, [] = _IOVec, _EFlags, _Timeout) -> -%% ok; -%% send_iovec({socket, _, SockRef} = Socket, [Bin|IOVec], EFlags, Timeout) -> -%% case do_send(SockRef, make_ref(), Bin, EFlags, Timeout) of -%% {ok, NewTimeout} -> -%% send_iovec(Socket, IOVec, EFlags, NewTimeout); -%% {error, _} = ERROR -> -%% ERROR -%% end. - - -%% do_send(SockRef, SendRef, Data, _EFlags, Timeout) -%% when (Timeout < 0) -> -%% nif_cancel(SockRef, SendRef), -%% flush_select_msgs(SockRef, SendRef), -%% {error, {timeout, size(Data)}}; -%% do_send(SockRef, SendRef, Data, EFlags, Timeout) -> -%% TS = timestamp(Timeout), -%% case nif_send(SockRef, SendRef, Data, EFlags) of -%% ok -> -%% {ok, next_timeout(TS, Timeout)}; -%% {ok, Written} -> -%% %% We are partially done, wait for continuation -%% receive -%% {select, SockRef, SendRef, ready_output} -> -%% <<_:Written/binary, Rest/binary>> = Data, -%% do_send(SockRef, make_ref(), Rest, EFlags, -%% next_timeout(TS, Timeout)) -%% after Timeout -> -%% nif_cancel(SockRef, SendRef), -%% flush_select_msgs(SockRef, SendRef), -%% {error, timeout} -%% end; -%% {error, eagain} -> -%% receive -%% {select, SockRef, SendRef, ready_output} -> -%% do_send(SockRef, SendRef, Data, EFlags, -%% next_timeout(TS, Timeout)) -%% after Timeout -> -%% nif_cancel(SockRef, SendRef), -%% flush_select_msgs(SockRef, SendRef), -%% {error, timeout} -%% end; - -%% {error, _} = ERROR -> -%% ERROR -%% end. %% =========================================================================== @@ -1695,8 +1632,7 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) after NewTimeout -> - nif_cancel(SockRef, recv, RecvRef), - flush_select_msgs(SockRef, RecvRef), + cancel(SockRef, recv, RecvRef), {error, {timeout, Acc}} end; @@ -1715,8 +1651,7 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) after NewTimeout -> - nif_cancel(SockRef, recv, RecvRef), - flush_select_msgs(SockRef, RecvRef), + cancel(SockRef, recv, RecvRef), {error, {timeout, Acc}} end; @@ -1739,8 +1674,7 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) {error, Reason} after NewTimeout -> - nif_cancel(SockRef, recv, RecvRef), - flush_select_msgs(SockRef, RecvRef), + cancel(SockRef, recv, RecvRef), {error, timeout} end; @@ -1765,7 +1699,7 @@ do_recv(SockRef, RecvRef, 0 = _Length, _Eflags, Acc, _Timeout) -> %% The current recv operation is to be cancelled, so no need for a ref... %% The cancel will end our 'read everything you have' and "activate" %% any waiting reader. - nif_cancel(SockRef, recv, RecvRef), + cancel(SockRef, recv, RecvRef), {ok, Acc}; do_recv(_SockRef, _RecvRef, _Length, _EFlags, Acc, _Timeout) when (size(Acc) > 0) -> {error, {timeout, Acc}}; @@ -1878,8 +1812,7 @@ do_recvfrom(SockRef, BufSz, EFlags, Timeout) -> {error, Reason} after NewTimeout -> - nif_cancel(SockRef, recvfrom, RecvRef), - flush_select_msgs(SockRef, RecvRef), + cancel(SockRef, recvfrom, RecvRef), {error, timeout} end; @@ -1966,8 +1899,7 @@ do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Timeout) -> {error, Reason} after NewTimeout -> - nif_cancel(SockRef, recvmsg, RecvRef), - flush_select_msgs(SockRef, RecvRef), + cancel(SockRef, recvmsg, RecvRef), {error, timeout} end; @@ -3325,10 +3257,19 @@ ensure_sockaddr(_SockAddr) -> -flush_select_msgs(LSRef, Ref) -> +cancel(SockRef, Op, OpRef) -> + case nif_cancel(SockRef, Op, OpRef) of + %% The select has already completed + {error, select_sent} -> + flush_select_msgs(SockRef, OpRef); + Other -> + Other + end. + +flush_select_msgs(SockRef, Ref) -> receive - {select, LSRef, Ref, _} -> - flush_select_msgs(LSRef, Ref) + {select, SockRef, Ref, _} -> + flush_select_msgs(SockRef, Ref) after 0 -> ok end. -- cgit v1.2.3 From 13d10bc60a41f98647d802524ea8ef8fa9af6b39 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 20 Sep 2018 15:37:37 +0200 Subject: [socket-nif] Add proper send timeout handling Added proper send timeout handling. Made use of the enif_select(mode = cancel) feature. Each time a timeout expires, the "active" send (the surrent write select) has to be cancelled. OTP-14831 --- erts/preloaded/src/socket.erl | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 1c16c94711..652054457f 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -1345,10 +1345,19 @@ sendto(Socket, Data, Dest) -> Data :: binary(), Dest :: null | sockaddr(), Flags :: send_flags(), - Reason :: term(). + Reason :: term() + ; (Socket, Data, Dest, Timeout) -> ok | {error, Reason} when + Socket :: socket(), + Data :: iodata(), + Dest :: null | sockaddr(), + Timeout :: timeout(), + Reason :: term(). + +sendto(Socket, Data, Dest, Flags) when is_list(Flags) -> + sendto(Socket, Data, Dest, Flags, ?SOCKET_SENDTO_TIMEOUT_DEFAULT); +sendto(Socket, Data, Dest, Timeout) -> + sendto(Socket, Data, Dest, ?SOCKET_SENDTO_FLAGS_DEFAULT, Timeout). -sendto(Socket, Data, Dest, Flags) -> - sendto(Socket, Data, Dest, Flags, ?SOCKET_SENDTO_TIMEOUT_DEFAULT). -spec sendto(Socket, Data, Dest, Flags, Timeout) -> ok | {error, Reason} when Socket :: socket(), -- cgit v1.2.3 From 1c412c62ba3be17b7a818f264049a7ee7942351e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 28 Sep 2018 18:40:08 +0200 Subject: [socket-nif] Add support for socket (level otp) buffer options Add support for otp level socket options rcvbuf, rcvctrlbuf and sndctrlbuf. These options define default sizes for these buffers. The 'rcvbuf' is used when receiving messages when calling the recv, recvfrom and recvmsg functions. The 'rcvctrlbuf' is used for the control message header info when calling the recvmsg function. The 'sndctrlbuf' is used for the control message header info when calling the sendmsg function. OTP-14831 --- erts/preloaded/src/socket.erl | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 652054457f..8093bad885 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -320,12 +320,15 @@ %% Should we just document it and leave it to the user? %% Or catch it in the encode functions? %% A setopt for a readonly option leads to einval? +%% Do we really need a sndbuf? -type otp_socket_option() :: debug | iow | controlling_process | rcvbuf | - sndbuf. + sndbuf | + rcvctrlbuf | + sndctrlbuf. %% Shall we have special treatment of linger?? %% read-only options: %% domain | protocol | type. @@ -645,6 +648,10 @@ -define(SOCKET_OPT_OTP_DEBUG, 1). -define(SOCKET_OPT_OTP_IOW, 2). -define(SOCKET_OPT_OTP_CTRL_PROC, 3). +-define(SOCKET_OPT_OTP_RCVBUF, 4). +%%-define(SOCKET_OPT_OTP_SNDBUF, 5). +-define(SOCKET_OPT_OTP_RCVCTRLBUF, 6). +-define(SOCKET_OPT_OTP_SNDCTRLBUF, 7). %% *** SOCKET (socket) options -define(SOCKET_OPT_SOCK_ACCEPTCONN, 1). @@ -2136,9 +2143,9 @@ getopt(#socket{ref = SockRef}, Level, Key) -> end end catch - throw:T -> - T; - error:Reason -> + throw:E:_S -> + E; + error:Reason:_Stack -> {error, Reason} % Process more? end. @@ -2401,6 +2408,18 @@ enc_setopt_value(otp, iow, V, _, _, _) when is_boolean(V) -> V; enc_setopt_value(otp, controlling_process, V, _, _, _) when is_pid(V) -> V; +enc_setopt_value(otp, rcvbuf, V, _, _, _) when (V =:= default) -> + 0; +enc_setopt_value(otp, rcvbuf, V, _, _, _) when is_integer(V) andalso (V > 0) -> + V; +enc_setopt_value(otp, rcvctrlbuf, V, _, _, _) when (V =:= default) -> + 0; +enc_setopt_value(otp, rcvctrlbuf, V, _, _, _) when is_integer(V) andalso (V > 0) -> + V; +enc_setopt_value(otp, sndctrlbuf, V, _, _, _) when (V =:= default) -> + 0; +enc_setopt_value(otp, sndctrlbuf, V, _, _, _) when is_integer(V) andalso (V > 0) -> + V; enc_setopt_value(otp = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); @@ -2871,6 +2890,12 @@ enc_sockopt_key(otp, iow, _, _, _, _) -> ?SOCKET_OPT_OTP_IOW; enc_sockopt_key(otp, controlling_process, _, _, _, _) -> ?SOCKET_OPT_OTP_CTRL_PROC; +enc_sockopt_key(otp, rcvbuf, _, _, _, _) -> + ?SOCKET_OPT_OTP_RCVBUF; +enc_sockopt_key(otp, rcvctrlbuf, _, _, _, _) -> + ?SOCKET_OPT_OTP_RCVCTRLBUF; +enc_sockopt_key(otp, sndctrlbuf, _, _, _, _) -> + ?SOCKET_OPT_OTP_SNDCTRLBUF; enc_sockopt_key(otp = L, Opt, _, _, _, _) -> not_supported({L, Opt}); -- cgit v1.2.3 From 4b1e1e148a5cc9c19127b61aaa3e15eeeaea6cb2 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 4 Oct 2018 10:06:16 +0200 Subject: [socket-nif] Socket option 'SO_DOMAIN' not avalable on all platforms Internally in the socket module we accessed domain, type and protocol for an open socket. But as it turns out, domain (family) is not actually available as a socket option on all platforms. FreeBSD in this case. So, since we store these values in the socket descriptor anyway, we switch to use these values for our internal use instead. OTP-14831 --- erts/preloaded/src/socket.erl | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 8093bad885..c388fc2849 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -652,6 +652,9 @@ %%-define(SOCKET_OPT_OTP_SNDBUF, 5). -define(SOCKET_OPT_OTP_RCVCTRLBUF, 6). -define(SOCKET_OPT_OTP_SNDCTRLBUF, 7). +-define(SOCKET_OPT_OTP_DOMAIN, 16#FF01). % INTERNAL +-define(SOCKET_OPT_OTP_TYPE, 16#FF02). % INTERNAL +-define(SOCKET_OPT_OTP_PROTOCOL, 16#FF03). % INTERNAL %% *** SOCKET (socket) options -define(SOCKET_OPT_SOCK_ACCEPTCONN, 1). @@ -2159,7 +2162,7 @@ getopt(#socket{ref = SockRef}, Level, Key) -> which_domain(SockRef) -> case nif_getopt(SockRef, true, - ?SOCKET_OPT_LEVEL_SOCKET, ?SOCKET_OPT_SOCK_DOMAIN) of + ?SOCKET_OPT_LEVEL_OTP, ?SOCKET_OPT_OTP_DOMAIN) of {ok, Domain} -> Domain; {error, _} = ERROR -> @@ -2173,7 +2176,7 @@ which_domain(SockRef) -> which_type(SockRef) -> case nif_getopt(SockRef, true, - ?SOCKET_OPT_LEVEL_SOCKET, ?SOCKET_OPT_SOCK_TYPE) of + ?SOCKET_OPT_LEVEL_OTP, ?SOCKET_OPT_OTP_TYPE) of {ok, Type} -> Type; {error, _} = ERROR -> @@ -2186,9 +2189,9 @@ which_type(SockRef) -> which_protocol(SockRef) -> case nif_getopt(SockRef, true, - ?SOCKET_OPT_LEVEL_SOCKET, ?SOCKET_OPT_SOCK_PROTOCOL) of - {ok, Type} -> - Type; + ?SOCKET_OPT_LEVEL_OTP, ?SOCKET_OPT_OTP_PROTOCOL) of + {ok, Proto} -> + Proto; {error, _} = ERROR -> throw(ERROR) end. -- cgit v1.2.3 From 6e931258872c15404aa1dfd5198f2b490452b30b Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 5 Oct 2018 14:52:47 +0200 Subject: [socket-nif] Add *preliminary* new function supports/0,1 --- erts/preloaded/src/socket.erl | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index c388fc2849..147d074310 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -27,6 +27,7 @@ -export([ on_load/0, on_load/1, info/0, + supports/0, supports/1, ensure_sockaddr/1 ]). @@ -843,6 +844,16 @@ info() -> nif_info(). +-spec supports() -> list(). + +supports() -> + [{options, supports(options)}]. + +supports(options) -> + nif_supports(?SOCKET_SUPPORTS_OPTIONS); +supports(_) -> + false. + %% =========================================================================== %% @@ -3417,6 +3428,9 @@ error(Reason) -> nif_info() -> erlang:nif_error(undef). +nif_supports(_Key) -> + erlang:nif_error(undef). + nif_open(_Domain, _Type, _Protocol, _Extra) -> erlang:nif_error(undef). -- cgit v1.2.3 From 09ada8ef363de23dff9af58b2284ea8aaf399071 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 5 Oct 2018 18:46:13 +0200 Subject: [socket-nif] Add first three socket (socket) options to supports Added the first three socket (level socket) options to the supports function(s). OTP-14831 --- erts/preloaded/src/socket.erl | 3 +++ 1 file changed, 3 insertions(+) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 147d074310..5f23bcce84 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -817,6 +817,8 @@ -define(SOCKET_SHUTDOWN_HOW_READ_WRITE, 2). +-define(SOCKET_SUPPORTS_OPTIONS, 16#0001). + %% =========================================================================== %% @@ -855,6 +857,7 @@ supports(_) -> false. + %% =========================================================================== %% %% The proper socket API -- cgit v1.2.3 From f1c6c983a7f21b55c597279e806b5df8f38d4947 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 8 Oct 2018 15:32:37 +0200 Subject: [socket-nif] Add the options for socket, ip and ipv6 for supports Add the options for level socket (full), ip and ipv6 to the supports function. OTP-14831 --- erts/preloaded/src/socket.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 5f23bcce84..882e305b26 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -734,8 +734,8 @@ %% -define(SOCKET_OPT_IPV6_CHECKSUM, 5). % FreeBSD -define(SOCKET_OPT_IPV6_DROP_MEMBERSHIP, 6). -define(SOCKET_OPT_IPV6_DSTOPTS, 7). -%% -define(SOCKET_OPT_IPV6_ESP_TRANS_LEVEL, 8). % FreeBSD -%% -define(SOCKET_OPT_IPV6_ESP_NETWORK_LEVEL, 9). % FreeBSD +%% -define(SOCKET_OPT_IPV6_ESP_NETWORK_LEVEL, 8). % FreeBSD +%% -define(SOCKET_OPT_IPV6_ESP_TRANS_LEVEL, 9). % FreeBSD %% -define(SOCKET_OPT_IPV6_FAITH, 10). % FreeBSD -define(SOCKET_OPT_IPV6_FLOWINFO, 11). -define(SOCKET_OPT_IPV6_HOPLIMIT, 12). -- cgit v1.2.3 From 789d5cefd0f710fd83dec27ad2239ea299737a49 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 9 Oct 2018 12:23:59 +0200 Subject: [socket-nif] The supports function now also handles sctp and ipv6 The supports function now also handles testing for SCTP and IPv6 support. Basic test at the moment, but better then nothing. OTP-14831 --- erts/preloaded/src/socket.erl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 882e305b26..d0e074c817 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -818,6 +818,8 @@ -define(SOCKET_SUPPORTS_OPTIONS, 16#0001). +-define(SOCKET_SUPPORTS_SCTP, 16#0002). +-define(SOCKET_SUPPORTS_IPV6, 16#0003). %% =========================================================================== @@ -849,10 +851,16 @@ info() -> -spec supports() -> list(). supports() -> - [{options, supports(options)}]. + [{options, supports(options)}, + {sctp, supports(sctp)}, + {ipv6, supports(ipv6)}]. supports(options) -> nif_supports(?SOCKET_SUPPORTS_OPTIONS); +supports(sctp) -> + nif_supports(?SOCKET_SUPPORTS_SCTP); +supports(ipv6) -> + nif_supports(?SOCKET_SUPPORTS_IPV6); supports(_) -> false. -- cgit v1.2.3 From ce28d70c686f342fb04fc05e1d00501e7cfbf213 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 9 Oct 2018 16:32:31 +0200 Subject: [socket-nif|doc] Add preliminary doc for the function supports Added preliminary documentation for the function socket:supports/0,1,2,3. It still does not generate proper doc for supports/3 (the last arg, Opt, don't get a type). OTP-14831 --- erts/preloaded/src/socket.erl | 83 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 3 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index d0e074c817..acf5e18cec 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -27,7 +27,7 @@ -export([ on_load/0, on_load/1, info/0, - supports/0, supports/1, + supports/0, supports/1, supports/2, supports/3, ensure_sockaddr/1 ]). @@ -848,20 +848,97 @@ info() -> nif_info(). --spec supports() -> list(). + +%% =========================================================================== +%% +%% supports - get information about what the platform "supports". +%% +%% Generates a list of various info about what the plaform can support. +%% The most obvious case is 'options'. +%% +%% Each item in a 'supports'-list will appear only *one* time. +%% +%% =========================================================================== + +-type supports_options_socket() :: [{socket_option(), boolean()}]. +-type supports_options_ip() :: [{ip_socket_option(), boolean()}]. +-type supports_options_ipv6() :: [{ipv6_socket_option(), boolean()}]. +-type supports_options_tcp() :: [{tcp_socket_option(), boolean()}]. +-type supports_options_udp() :: [{udp_socket_option(), boolean()}]. +-type supports_options_sctp() :: [{sctp_socket_option(), boolean()}]. +-type supports_options() :: [{socket, supports_options_socket()} | + {ip, supports_options_ip()} | + {ipv6, supports_options_ipv6()} | + {tcp, supports_options_tcp()} | + {udp, supports_options_udp()} | + {sctp, supports_options_sctp()}]. + +-spec supports() -> [{options, supports_options()} | + {sctp, boolean()} | + {ipv6, boolean()}]. supports() -> [{options, supports(options)}, {sctp, supports(sctp)}, {ipv6, supports(ipv6)}]. + +-spec supports(options) -> supports_options(); + (sctp) -> boolean(); + (ipv6) -> boolean(); + (Key1) -> false when + Key1 :: term(). + supports(options) -> nif_supports(?SOCKET_SUPPORTS_OPTIONS); supports(sctp) -> nif_supports(?SOCKET_SUPPORTS_SCTP); supports(ipv6) -> nif_supports(?SOCKET_SUPPORTS_IPV6); -supports(_) -> +supports(_Key1) -> + false. + +-spec supports(options, socket) -> supports_options_socket(); + (options, ip) -> supports_options_ip(); + (options, ipv6) -> supports_options_ipv6(); + (options, tcp) -> supports_options_tcp(); + (options, udp) -> supports_options_udp(); + (options, sctp) -> supports_options_sctp(); + (Key1, Key2) -> false when + Key1 :: term(), + Key2 :: term(). + +supports(options, Level) -> + proplists:get_value(Level, supports(options), false); +supports(_Key1, _Level) -> + false. + + +-spec supports(options, socket, Opt) -> boolean() when + Opt :: socket_option(); + (options, ip, Opt) -> boolean() when + Opt :: ip_socket_option(); + (options, ipv6, Opt) -> boolean() when + Opt :: ipv6_socket_option(); + (options, tcp, Opt) -> boolean() when + Opt :: tcp_socket_option(); + (options, udp, Opt) -> boolean() when + Opt :: udp_socket_option(); + (options, sctp, Opt) -> boolean() when + Opt :: sctp_socket_option(); + (Key1, Key2, Key3) -> false when + Key1 :: term(), + Key2 :: term(), + Key3 :: term(). + +supports(options, Level, Opt) -> + case supports(options, Level) of + S when is_list(S) -> + proplists:get_value(Opt, S, false); + _ -> + false + end; +supports(_Key1, _Key2, _Key3) -> false. -- cgit v1.2.3 From 3f46e8a184a503ead01674ee180e7222b3928712 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 30 Oct 2018 18:26:10 +0100 Subject: [socket-nif] Add a send and receive chunks test case The send and recv test case triggered a two bugs. One was that there was no re-selecting when only a portion of the data was received (which meant that we stopped reading). Also, the wrong 'current' (writer) was reset when demonitor current reader after a successful read (which meant that future readers would never have been monitored). OTP-14831 --- erts/preloaded/src/socket.erl | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index acf5e18cec..5ebc2074e0 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -1716,10 +1716,14 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) (is_integer(Timeout) andalso (Timeout > 0)) -> TS = timestamp(Timeout), RecvRef = make_ref(), + %% p("do_recv -> try read with" + %% "~n Length: ~p", [Length]), case nif_recv(SockRef, RecvRef, Length, EFlags) of {ok, true = _Complete, Bin} when (size(Acc) =:= 0) -> + %% p("do_recv -> complete success: ~w", [size(Bin)]), {ok, Bin}; {ok, true = _Complete, Bin} -> + %% p("do_recv -> completed success: ~w (~w)", [size(Bin), size(Acc)]), {ok, <>}; %% It depends on the amount of bytes we tried to read: @@ -1728,6 +1732,7 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) %% > 0 - We got a part of the message and we will be notified %% when there is more to read (a select message) {ok, false = _Complete, Bin} when (Length =:= 0) -> + %% p("do_recv -> partial success: ~w", [size(Bin)]), do_recv(SockRef, RecvRef, Length, EFlags, <>, @@ -1737,6 +1742,8 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) %% We got the first chunk of it. %% We will be notified (select message) when there %% is more to read. + %% p("do_recv -> partial success(~w): ~w" + %% "~n ~p", [Length, size(Bin), Bin]), NewTimeout = next_timeout(TS, Timeout), receive {select, SockRef, RecvRef, ready_input} -> @@ -1756,6 +1763,8 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) {ok, false = _Completed, Bin} -> %% We got a chunk of it! + %% p("do_recv -> partial success(~w): ~w (~w)", + %% [Length, size(Bin), size(Acc)]), NewTimeout = next_timeout(TS, Timeout), receive {select, SockRef, RecvRef, ready_input} -> @@ -1780,6 +1789,7 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) {error, eagain} -> %% There is nothing just now, but we will be notified when there %% is something to read (a select message). + %% p("do_recv -> eagain(~w): ~w", [Length, size(Acc)]), NewTimeout = next_timeout(TS, Timeout), receive {select, SockRef, RecvRef, ready_input} -> @@ -3473,6 +3483,7 @@ tdiff(T1, T2) -> %% p(undefined, F, A) -> %% p("***", F, A); %% p(SName, F, A) -> +%% io:format(user,"[~s,~p] " ++ F ++ "~n", [SName, self()|A]), %% io:format("[~s,~p] " ++ F ++ "~n", [SName, self()|A]). -- cgit v1.2.3 From f18bd2a88d7bc8519cf5db611c4c530eedecba2a Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 5 Nov 2018 14:43:07 +0100 Subject: [socket-nif] Add "partial success" to sendmsg The sendmsg function attempts to send *one message*. But its possible for the underlying software to fail to send the *entire* message. So, instead of retrying itself, as send does, the sendmsg function will now instead return with {ok, Remaining}, leaving it to the caller to decide what to do. OTP-14831 --- erts/preloaded/src/socket.erl | 47 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 8 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 5ebc2074e0..dd10aac3ff 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -1573,12 +1573,13 @@ sendmsg(Socket, MsgHdr, Timeout) sendmsg(Socket, MsgHdr, ?SOCKET_SENDMSG_FLAGS_DEFAULT, Timeout). --spec sendmsg(Socket, MsgHdr, Flags, Timeout) -> ok | {error, Reason} when - Socket :: socket(), - MsgHdr :: msghdr(), - Flags :: send_flags(), - Timeout :: timeout(), - Reason :: term(). +-spec sendmsg(Socket, MsgHdr, Flags, Timeout) -> ok | {ok, Remaining} | {error, Reason} when + Socket :: socket(), + MsgHdr :: msghdr(), + Flags :: send_flags(), + Timeout :: timeout(), + Remaining :: erlang:iovec(), + Reason :: term(). sendmsg(#socket{ref = SockRef}, #{iov := IOV} = MsgHdr, Flags, Timeout) when is_list(IOV) andalso @@ -1603,6 +1604,18 @@ do_sendmsg(SockRef, MsgHdr, EFlags, Timeout) -> %% We are done ok; + {ok, Written} when is_integer(Written) andalso (Written > 0) -> + + %% We should not retry here since the protocol may not + %% be able to handle a message being split. Leave it to + %% the caller to figure out (call again with the rest). + %% + %% We should really not need to cancel, since this is + %% accepted for sendmsg! + %% + cancel(SockRef, sendmsg, SendRef), + {ok, do_sendmsg_rest(maps:get(iov, MsgHdr), Written)}; + {error, eagain} -> receive {select, SockRef, SendRef, ready_output} -> @@ -1617,6 +1630,12 @@ do_sendmsg(SockRef, MsgHdr, EFlags, Timeout) -> ERROR end. +do_sendmsg_rest([B|IOVec], Written) when (Written >= size(B)) -> + do_sendmsg_rest(IOVec, Written - size(B)); +do_sendmsg_rest([B|IOVec], Written) -> + <<_:Written/binary, Rest/binary>> = B, + [Rest|IOVec]. + ensure_msghdr(#{ctrl := []} = M) -> ensure_msghdr(maps:remove(ctrl, M)); ensure_msghdr(#{iov := IOV} = M) when is_list(IOV) andalso (IOV =/= []) -> @@ -1625,6 +1644,8 @@ ensure_msghdr(_) -> einval(). + + %% =========================================================================== %% %% writev - write data into multiple buffers @@ -1983,10 +2004,20 @@ recvmsg(Socket, Timeout) -> Flags :: recv_flags(), Timeout :: timeout(), MsgHdr :: msghdr(), + Reason :: term() + ; (Socket, BufSz, CtrlSz) -> {ok, MsgHdr} | {error, Reason} when + Socket :: socket(), + BufSz :: non_neg_integer(), + CtrlSz :: non_neg_integer(), + MsgHdr :: msghdr(), Reason :: term(). -recvmsg(Socket, Flags, Timeout) -> - recvmsg(Socket, 0, 0, Flags, Timeout). +recvmsg(Socket, Flags, Timeout) when is_list(Flags) -> + recvmsg(Socket, 0, 0, Flags, Timeout); +recvmsg(Socket, BufSz, CtrlSz) when is_integer(BufSz) andalso is_integer(CtrlSz) -> + recvmsg(Socket, BufSz, CtrlSz, + ?SOCKET_RECV_FLAGS_DEFAULT, ?SOCKET_RECV_TIMEOUT_DEFAULT). + -spec recvmsg(Socket, BufSz, CtrlSz, -- cgit v1.2.3 From e3e607ac76dc308da3ac24364477d48da0dc23bd Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 6 Nov 2018 15:22:09 +0100 Subject: [socket-nif|test] Add UDP ping-pong test cases Added ping-pong test cases for UDP, small and medium, using the sendto/recvfrom and sendmsg/recvmsg functions. OTP-14831 --- erts/preloaded/src/socket.erl | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index dd10aac3ff..a40692881b 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -1958,6 +1958,7 @@ do_recvfrom(SockRef, BufSz, EFlags, Timeout) -> next_timeout(TS, Timeout)); {nif_abort, RecvRef, Reason} -> + %% p("received nif-abort: ~p", [Reason]), {error, Reason} after NewTimeout -> @@ -1970,6 +1971,13 @@ do_recvfrom(SockRef, BufSz, EFlags, Timeout) -> end. +%% pi(Item) -> +%% pi(self(), Item). + +%% pi(Pid, Item) -> +%% {Item, Info} = process_info(Pid, Item), +%% Info. + %% --------------------------------------------------------------------------- %% -- cgit v1.2.3 From 342d35f457c15a9cea426e8ca83bfd52b0ec2f2e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 12 Dec 2018 18:37:55 +0100 Subject: [socket-nif] Message interface between socket.erl and nif updated Previously the "message interface" between the functions in socket.erl and the nif-code (socket_nif.c) was "ad hoc". This has now been changed so that we have a unified message {'$socket', SockRef | undefined, Tag, Info} This also has the added advantage of preparing the code for when we start using the new select-fucntions (with which its possible to specify your own message). This will be used in order to get around our eterm "leak" (we will use a simple counter, maintained in the nif, instead of the [Recv|Send|Acc]Ref we generate in the erlang code today. OTP-14831 --- erts/preloaded/src/socket.erl | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index a40692881b..2e295a91ae 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -1331,7 +1331,7 @@ do_accept(LSockRef, Timeout) -> {select, LSockRef, AccRef, ready_input} -> do_accept(LSockRef, next_timeout(TS, Timeout)); - {nif_abort, AccRef, Reason} -> + {'$socket', _, abort, {AccRef, Reason}} -> {error, Reason} after NewTimeout -> @@ -1408,7 +1408,7 @@ do_send(SockRef, Data, EFlags, Timeout) -> do_send(SockRef, Data, EFlags, next_timeout(TS, Timeout)); - {nif_abort, SendRef, Reason} -> + {'$socket', _, abort, {SendRef, Reason}} -> {error, Reason} after NewTimeout -> @@ -1421,7 +1421,7 @@ do_send(SockRef, Data, EFlags, Timeout) -> do_send(SockRef, Data, EFlags, next_timeout(TS, Timeout)); - {nif_abort, SendRef, Reason} -> + {'$socket', _, abort, {SendRef, Reason}} -> {error, Reason} after Timeout -> @@ -1513,7 +1513,7 @@ do_sendto(SockRef, Data, Dest, EFlags, Timeout) -> do_sendto(SockRef, Data, Dest, EFlags, next_timeout(TS, Timeout)); - {nif_abort, SendRef, Reason} -> + {'$socket', _, abort, {SendRef, Reason}} -> {error, Reason} after Timeout -> @@ -1525,7 +1525,11 @@ do_sendto(SockRef, Data, Dest, EFlags, Timeout) -> receive {select, SockRef, SendRef, ready_output} -> do_sendto(SockRef, Data, Dest, EFlags, - next_timeout(TS, Timeout)) + next_timeout(TS, Timeout)); + + {'$socket', _, abort, {SendRef, Reason}} -> + {error, Reason} + after Timeout -> cancel(SockRef, sendto, SendRef), {error, timeout} @@ -1773,10 +1777,9 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) Bin, next_timeout(TS, Timeout)); - {nif_abort, RecvRef, Reason} -> + {'$socket', _, abort, {RecvRef, Reason}} -> {error, Reason} - after NewTimeout -> cancel(SockRef, recv, RecvRef), {error, {timeout, Acc}} @@ -1794,10 +1797,9 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) <>, next_timeout(TS, Timeout)); - {nif_abort, RecvRef, Reason} -> + {'$socket', _, abort, {RecvRef, Reason}} -> {error, Reason} - after NewTimeout -> cancel(SockRef, recv, RecvRef), {error, {timeout, Acc}} @@ -1805,6 +1807,7 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) %% We return with the accumulated binary (if its non-empty) {error, eagain} when (Length =:= 0) andalso (size(Acc) > 0) -> + %% CAN WE REALLY DO THIS? THE NIF HAS SELECTED!! OR? {ok, Acc}; {error, eagain} -> @@ -1819,7 +1822,7 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) Acc, next_timeout(TS, Timeout)); - {nif_abort, RecvRef, Reason} -> + {'$socket', _, abort, {RecvRef, Reason}} -> {error, Reason} after NewTimeout -> @@ -1850,7 +1853,8 @@ do_recv(SockRef, RecvRef, 0 = _Length, _Eflags, Acc, _Timeout) -> %% any waiting reader. cancel(SockRef, recv, RecvRef), {ok, Acc}; -do_recv(_SockRef, _RecvRef, _Length, _EFlags, Acc, _Timeout) when (size(Acc) > 0) -> +do_recv(_SockRef, _RecvRef, _Length, _EFlags, Acc, _Timeout) + when (size(Acc) > 0) -> {error, {timeout, Acc}}; do_recv(_SockRef, _RecvRef, _Length, _EFlags, _Acc, _Timeout) -> {error, timeout}. @@ -1957,8 +1961,7 @@ do_recvfrom(SockRef, BufSz, EFlags, Timeout) -> do_recvfrom(SockRef, BufSz, EFlags, next_timeout(TS, Timeout)); - {nif_abort, RecvRef, Reason} -> - %% p("received nif-abort: ~p", [Reason]), + {'$socket', _, abort, {RecvRef, Reason}} -> {error, Reason} after NewTimeout -> @@ -2062,7 +2065,7 @@ do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Timeout) -> do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, next_timeout(TS, Timeout)); - {nif_abort, RecvRef, Reason} -> + {'$socket', _, abort, {RecvRef, Reason}} -> {error, Reason} after NewTimeout -> @@ -2107,7 +2110,8 @@ do_close(SockRef) -> {ok, CloseRef} -> %% We must wait receive - {close, CloseRef} -> + {'$socket', _, close, CloseRef} -> +%% {close, CloseRef} -> %% %% %% WHAT HAPPENS IF THIS PROCESS IS KILLED -- cgit v1.2.3 From 8a820377bb3e2e506cdff6011057d78507544142 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 21 Jan 2019 14:46:07 +0100 Subject: [socket-nif] Add support for otp option fd Add a way to *get* the file descriptor (fd) of a socket. Useful mostly for debugging. OTP-15528 --- erts/preloaded/src/socket.erl | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 2e295a91ae..839087ef2a 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2018-2018. All Rights Reserved. +%% Copyright Ericsson AB 2018-2019. 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. @@ -41,12 +41,10 @@ send/2, send/3, send/4, sendto/3, sendto/4, sendto/5, sendmsg/2, sendmsg/3, sendmsg/4, - %% writev/4, OR SENDV? It will be strange for recv then: recvv (instead of readv) recv/1, recv/2, recv/3, recv/4, recvfrom/1, recvfrom/2, recvfrom/3, recvfrom/4, recvmsg/1, recvmsg/2, recvmsg/3, recvmsg/5, - %% readv/3, close/1, shutdown/2, @@ -164,16 +162,6 @@ 0..65535, 0..65535}. -%% -%% -%% Should we do these as maps instead? -%% If we do we may need to include the family (domain) in the -%% map (as the native type do. See struct sockaddr_in6). -%% -%% What about default values? Such as for port (=0) and addr (=any)? -%% -%% - -type timeval() :: #{sec := integer(), usec := integer()}. @@ -183,7 +171,7 @@ addr := ip4_address() % Header Destination address }. -%% If the integer value is used its up to the caller to ensure its valid! +%% If the integer value is used, its up to the caller to ensure its valid! -type ip_tos() :: lowdeley | throughput | reliability | @@ -326,10 +314,10 @@ -type otp_socket_option() :: debug | iow | controlling_process | - rcvbuf | - sndbuf | + rcvbuf | % sndbuf | rcvctrlbuf | - sndctrlbuf. + sndctrlbuf | + fd. %% Shall we have special treatment of linger?? %% read-only options: %% domain | protocol | type. @@ -653,6 +641,7 @@ %%-define(SOCKET_OPT_OTP_SNDBUF, 5). -define(SOCKET_OPT_OTP_RCVCTRLBUF, 6). -define(SOCKET_OPT_OTP_SNDCTRLBUF, 7). +-define(SOCKET_OPT_OTP_FD, 8). -define(SOCKET_OPT_OTP_DOMAIN, 16#FF01). % INTERNAL -define(SOCKET_OPT_OTP_TYPE, 16#FF02). % INTERNAL -define(SOCKET_OPT_OTP_PROTOCOL, 16#FF03). % INTERNAL @@ -3051,6 +3040,8 @@ enc_sockopt_key(otp, rcvctrlbuf, _, _, _, _) -> ?SOCKET_OPT_OTP_RCVCTRLBUF; enc_sockopt_key(otp, sndctrlbuf, _, _, _, _) -> ?SOCKET_OPT_OTP_SNDCTRLBUF; +enc_sockopt_key(otp, fd, get = _Dir, _, _, _) -> + ?SOCKET_OPT_OTP_FD; enc_sockopt_key(otp = L, Opt, _, _, _, _) -> not_supported({L, Opt}); -- cgit v1.2.3 From 3d166efe4f3ee6a93edf361a9d72633a00fb486f Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 29 Jan 2019 12:41:19 +0100 Subject: [socket-nif] The otp rcvbuf option updated Its now possible to set a rcvbuf (otp) option value of {N :: pos_integer(), BufSz :: pos_integer()}. This value is used for type stream and protocol tcp, when calling the function recv with length = 0 (zero). The second value, BufSz, is the actual size of the receive buffer used when calling the socket recv function, and the first value, N, is the max number of possible reads that will be performed (at most), even if there is more data to read. This is limit the effect of DoS attacks. OTP-15497 --- erts/preloaded/src/socket.erl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 839087ef2a..80ccd7ea10 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -2553,9 +2553,15 @@ enc_setopt_value(otp, iow, V, _, _, _) when is_boolean(V) -> enc_setopt_value(otp, controlling_process, V, _, _, _) when is_pid(V) -> V; enc_setopt_value(otp, rcvbuf, V, _, _, _) when (V =:= default) -> - 0; + 0; % This will cause the nif-code to choose the default value enc_setopt_value(otp, rcvbuf, V, _, _, _) when is_integer(V) andalso (V > 0) -> V; +%% N: Number of reads (when specifying length = 0) +%% V: Size of the "read" buffer +enc_setopt_value(otp, rcvbuf, {N, BufSz} = V, _, stream = _T, tcp = _P) + when (is_integer(N) andalso (N > 0)) andalso + (is_integer(BufSz) andalso (BufSz > 0)) -> + V; enc_setopt_value(otp, rcvctrlbuf, V, _, _, _) when (V =:= default) -> 0; enc_setopt_value(otp, rcvctrlbuf, V, _, _, _) when is_integer(V) andalso (V > 0) -> -- cgit v1.2.3 From 875825874d4a8d52ec5cc593f5024afc696c29df Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 31 Jan 2019 11:34:03 +0100 Subject: [socket-nif] nosup expection of win32 and type(s) replacements The nif callback functions (nif_open) now instead cause an 'nosup' exception if called (instead of badarg). The basic type uint16_t, uint32_t and int32_t (C99) replaced "own" (that is, defined by "us") types Uint16, Uint32 and Sint32. The point of this is that our Windows build system seems to be a bit lacking when it comes to types... Removed "some stuff" that was if-defed. Different solution when win32 support for sockets has been improved. Make sure the socket_*.c util modules are not included in the building for windows. OTP-15526 --- erts/preloaded/src/socket.erl | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 80ccd7ea10..287ef1d791 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -1055,6 +1055,10 @@ open(Domain, Type, Protocol0, Extra) when is_map(Extra) -> catch throw:T -> T; + %% + error:nosup:S -> + erlang:raise(error, nosup, S); + %% error:Reason -> {error, Reason} end. @@ -1088,6 +1092,10 @@ bind(#socket{ref = SockRef}, Addr) inet6 -> nif_bind(SockRef, ?SOCKADDR_IN6_DEFAULT(Addr)) catch + %% + error:nosup:S -> + erlang:raise(error, nosup, S); + %% throw:ERROR -> ERROR end; @@ -1097,6 +1105,10 @@ bind(#socket{ref = SockRef} = _Socket, Addr) when is_map(Addr) -> nif_bind(SockRef, ensure_sockaddr(Addr)) end catch + %% + error:nosup:S -> + erlang:raise(error, nosup, S); + %% throw:ERROR -> ERROR end. @@ -1131,6 +1143,10 @@ bind(#socket{ref = SockRef}, Addrs, Action) nif_bind(SockRef, Addrs, Action) end catch + %% + error:nosup:S -> + erlang:raise(error, nosup, S); + %% throw:ERROR -> ERROR end. @@ -2135,6 +2151,10 @@ shutdown(#socket{ref = SockRef}, How) -> catch throw:T -> T; + %% + error:nosup:S -> + erlang:raise(error, nosup, S); + %% error:Reason -> {error, Reason} end. @@ -2207,6 +2227,10 @@ setopt(#socket{ref = SockRef}, Level, Key, Value) -> catch throw:T -> T; + %% + error:nosup:S -> + erlang:raise(error, nosup, S); + %% error:Reason -> {error, Reason} % Process more? end. @@ -2289,6 +2313,10 @@ getopt(#socket{ref = SockRef}, Level, Key) -> catch throw:E:_S -> E; + %% + error:nosup:S -> + erlang:raise(error, nosup, S); + %% error:Reason:_Stack -> {error, Reason} % Process more? end. -- cgit v1.2.3