aboutsummaryrefslogtreecommitdiffstats
path: root/erts/preloaded/src/socket.erl
diff options
context:
space:
mode:
authorMicael Karlberg <[email protected]>2018-03-22 17:57:15 +0100
committerMicael Karlberg <[email protected]>2018-09-18 13:01:37 +0200
commit3ca71520bfb664f0ea809ffdf41505936e4d5e90 (patch)
tree1e14f5b647ac4e41d376e4ff47bb7570b4ea00bc /erts/preloaded/src/socket.erl
parent716d9c2256ddbba2417fd39053a891b870c43472 (diff)
downloadotp-3ca71520bfb664f0ea809ffdf41505936e4d5e90.tar.gz
otp-3ca71520bfb664f0ea809ffdf41505936e4d5e90.tar.bz2
otp-3ca71520bfb664f0ea809ffdf41505936e4d5e90.zip
[socket-nif] preliminary version of the new socket interface (nififying)
Diffstat (limited to 'erts/preloaded/src/socket.erl')
-rw-r--r--erts/preloaded/src/socket.erl878
1 files changed, 878 insertions, 0 deletions
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).