summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaimo Niskanen <raimo@erlang.org>2020-04-20 10:22:07 +0200
committerRaimo Niskanen <raimo@erlang.org>2020-04-20 10:22:07 +0200
commit667ce1a7b2c1b5a84593be3726654a095af374b1 (patch)
tree1e4be30b8fd4a8a1ca681e3169fcf1efd7f8d90b
parentbde3f6500ae37e146305a02d8d15b84cdd54527e (diff)
parentb275f25145e6e375c6cb713c2b672af2d84735a4 (diff)
downloaderlang-667ce1a7b2c1b5a84593be3726654a095af374b1.tar.gz
Merge branch 'raimo/split-socket-module/OTP-16312'
* raimo/split-socket-module/OTP-16312: Fix broken links in release notes Simplify code for connect() Fix syntax error for USE_ESOCK != yes Document type errcode() Try fix reading unintialized sockaddr Correct socket() type in inet Change socket wrapper tuple to use dollar tag Remove dead code Clean up return types Fix lock problems Clean up return values Use exceptions for fatal argument errors List the socket module in more places Use dedicated functions for nowait Do not use size/1 in kernel snmp: Use new API Fix documentation for supports/0-2 and is_supported/1-3 Fix dialyzer warnings Split socket module
-rw-r--r--erts/doc/src/Makefile30
-rw-r--r--erts/doc/src/notes.xml26
-rw-r--r--erts/doc/src/part.xml (renamed from erts/doc/src/part.xml.src)1
-rw-r--r--erts/doc/src/ref_man.xml (renamed from erts/doc/src/ref_man.xml.src)1
-rw-r--r--erts/doc/src/specs.xml (renamed from erts/doc/src/specs.xml.src)1
-rw-r--r--erts/emulator/Makefile.in7
-rw-r--r--erts/emulator/nifs/common/prim_socket_nif.c (renamed from erts/emulator/nifs/common/socket_nif.c)1356
-rw-r--r--erts/emulator/nifs/common/socket_int.h2
-rw-r--r--erts/emulator/test/Makefile24
-rw-r--r--erts/preloaded/ebin/erl_init.beambin2296 -> 2316 bytes
-rw-r--r--erts/preloaded/ebin/prim_net.beambin5140 -> 4900 bytes
-rw-r--r--erts/preloaded/ebin/prim_socket.beambin0 -> 30004 bytes
-rw-r--r--erts/preloaded/ebin/socket.beambin81284 -> 0 bytes
-rw-r--r--erts/preloaded/src/Makefile4
-rw-r--r--erts/preloaded/src/erl_init.erl4
-rw-r--r--erts/preloaded/src/erts.app.src2
-rw-r--r--erts/preloaded/src/prim_net.erl17
-rw-r--r--erts/preloaded/src/prim_socket.erl1302
-rw-r--r--erts/preloaded/src/socket.erl4192
-rw-r--r--lib/kernel/doc/src/Makefile5
-rw-r--r--lib/kernel/doc/src/part.xml1
-rw-r--r--lib/kernel/doc/src/ref_man.xml1
-rw-r--r--lib/kernel/doc/src/socket.xml (renamed from erts/doc/src/socket.xml)244
-rw-r--r--lib/kernel/doc/src/socket_usage.xml (renamed from erts/doc/src/socket_usage.xml)0
-rw-r--r--lib/kernel/doc/src/specs.xml1
-rw-r--r--lib/kernel/src/Makefile1
-rw-r--r--lib/kernel/src/gen_tcp.erl2
-rw-r--r--lib/kernel/src/gen_tcp_socket.erl4
-rw-r--r--lib/kernel/src/inet.erl5
-rw-r--r--lib/kernel/src/inet6_udp.erl6
-rw-r--r--lib/kernel/src/inet_udp.erl6
-rw-r--r--lib/kernel/src/kernel.app.src1
-rw-r--r--lib/kernel/src/net.erl47
-rw-r--r--lib/kernel/src/socket.erl2563
-rw-r--r--lib/kernel/test/Makefile22
-rw-r--r--lib/kernel/test/net_SUITE.erl19
-rw-r--r--lib/kernel/test/socket_SUITE.erl (renamed from erts/emulator/test/socket_SUITE.erl)86
-rw-r--r--lib/kernel/test/socket_test_evaluator.erl (renamed from erts/emulator/test/socket_test_evaluator.erl)0
-rw-r--r--lib/kernel/test/socket_test_evaluator.hrl (renamed from erts/emulator/test/socket_test_evaluator.hrl)0
-rw-r--r--lib/kernel/test/socket_test_lib.erl (renamed from erts/emulator/test/socket_test_lib.erl)2
-rw-r--r--lib/kernel/test/socket_test_logger.erl (renamed from erts/emulator/test/socket_test_logger.erl)0
-rw-r--r--lib/kernel/test/socket_test_ttest.hrl (renamed from erts/emulator/test/socket_test_ttest.hrl)0
-rw-r--r--lib/kernel/test/socket_test_ttest_client.hrl (renamed from erts/emulator/test/socket_test_ttest_client.hrl)0
-rw-r--r--lib/kernel/test/socket_test_ttest_lib.erl (renamed from erts/emulator/test/socket_test_ttest_lib.erl)0
-rw-r--r--lib/kernel/test/socket_test_ttest_tcp_client.erl (renamed from erts/emulator/test/socket_test_ttest_tcp_client.erl)0
-rw-r--r--lib/kernel/test/socket_test_ttest_tcp_client_gen.erl (renamed from erts/emulator/test/socket_test_ttest_tcp_client_gen.erl)0
-rw-r--r--lib/kernel/test/socket_test_ttest_tcp_client_socket.erl (renamed from erts/emulator/test/socket_test_ttest_tcp_client_socket.erl)0
-rw-r--r--lib/kernel/test/socket_test_ttest_tcp_gen.erl (renamed from erts/emulator/test/socket_test_ttest_tcp_gen.erl)0
-rw-r--r--lib/kernel/test/socket_test_ttest_tcp_server.erl (renamed from erts/emulator/test/socket_test_ttest_tcp_server.erl)0
-rw-r--r--lib/kernel/test/socket_test_ttest_tcp_server_gen.erl (renamed from erts/emulator/test/socket_test_ttest_tcp_server_gen.erl)0
-rw-r--r--lib/kernel/test/socket_test_ttest_tcp_server_socket.erl (renamed from erts/emulator/test/socket_test_ttest_tcp_server_socket.erl)0
-rw-r--r--lib/kernel/test/socket_test_ttest_tcp_socket.erl (renamed from erts/emulator/test/socket_test_ttest_tcp_socket.erl)0
-rw-r--r--lib/sasl/src/systools_make.erl19
-rw-r--r--lib/snmp/test/snmp_test_lib.erl2
54 files changed, 4871 insertions, 5135 deletions
diff --git a/erts/doc/src/Makefile b/erts/doc/src/Makefile
index 3e2eb80b50..41ade2bba7 100644
--- a/erts/doc/src/Makefile
+++ b/erts/doc/src/Makefile
@@ -47,20 +47,6 @@ XML_REF1_FILES = epmd_cmd.xml \
run_erl_cmd.xml \
start_cmd.xml
-ifeq ($(USE_ESOCK), yes)
-XML_REF3_ESOCK_EFILES = socket.xml
-XML_CHAPTER_ESOCK_EFILES = socket_usage.xml
-ESOCK_USE_SOCKET_XML=<xi:include href="socket.xml"\/>
-ESOCK_USE_SOCKET_SPECS_XML=<xi:include href="../specs/specs_socket.xml"/>
-ESOCK_USE_SOCKET_USAGE_XML=<xi:include href="socket_usage.xml"/>
-else
-XML_REF3_ESOCK_EFILES =
-XML_CHAPTER_ESOCK_EFILES =
-ESOCK_USE_SOCKET_XML =
-ESOCK_USE_SOCKET_SPECS_XML =
-ESOCK_USE_SOCKET_USAGE_XML =
-endif
-
XML_REF3_EFILES = \
erl_prim_loader.xml \
erlang.xml \
@@ -69,8 +55,7 @@ XML_REF3_EFILES = \
persistent_term.xml \
atomics.xml \
counters.xml \
- zlib.xml \
- $(XML_REF3_ESOCK_EFILES)
+ zlib.xml
XML_REF3_CREF = \
driver_entry.xml \
@@ -109,7 +94,6 @@ XML_CHAPTER_FILES = \
driver.xml \
absform.xml \
inet_cfg.xml \
- $(XML_CHAPTER_ESOCK_EFILES) \
erl_ext_dist.xml \
erl_dist_protocol.xml \
communication.xml \
@@ -171,18 +155,6 @@ $(XML_REF3_CREF:%.xml=$(SPECDIR)/specs_%.xml): $(@:%.xml=%.xml)
$(XMLDIR)/%.xml: ../../emulator/internal_doc/%.md $(ERL_TOP)/make/emd2exml
$(ERL_TOP)/make/emd2exml $< $@
-ref_man.xml: ref_man.xml.src
- ($(PERL) -p -e 's?%ESOCK_USE_SOCKET_XML%?$(ESOCK_USE_SOCKET_XML)?' \
- $<) > $@
-
-part.xml: part.xml.src
- ($(PERL) -p -e 's?%ESOCK_USE_SOCKET_USAGE_XML%?$(ESOCK_USE_SOCKET_USAGE_XML)?' \
- $<) > $@
-
-specs.xml: specs.xml.src
- ($(PERL) -p -e 's?%ESOCK_USE_SOCKET_SPECS_XML%?$(ESOCK_USE_SOCKET_SPECS_XML)?' \
- $<) > $@
-
# ----------------------------------------------------
# Release Target
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index 9c8890fc67..ba49a0339b 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -167,7 +167,7 @@
<list>
<item>
<p>
- For <seeerl marker="socket#">socket</seeerl>, not all
+ For <seeerl marker="kernel:socket#">socket</seeerl>, not all
send and receive flags are supported on all platforms. In
order to (at least) simplify testing, the
socket:supports/0,1,2,3 functions has been extended with
@@ -386,7 +386,7 @@
</item>
<item>
<p>
- For <seeerl marker="socket#">socket</seeerl>, invalid
+ For <seeerl marker="kernel:socket#">socket</seeerl>, invalid
encoding of send and receive flags caused badarg and send
failure.</p>
<p>
@@ -401,7 +401,7 @@
</item>
<item>
<p>
- For <seeerl marker="socket#">socket</seeerl>, unable to
+ For <seeerl marker="kernel:socket#">socket</seeerl>, unable to
properly decode the timestamp control message header on
FreeBSD. We incorrectly used the SO_TIMESTAMP flag for
the timestamp control message header type. It should have
@@ -412,7 +412,7 @@
</item>
<item>
<p>
- For <seeerl marker="socket#">socket</seeerl>, when
+ For <seeerl marker="kernel:socket#">socket</seeerl>, when
setting the ip option 'recvtos' to true, thereby
indicating that we want to receive the TOS control
message header, we don't actually get TOS but RECVTOS on
@@ -429,7 +429,7 @@
</item>
<item>
<p>
- For <seeerl marker="socket#">socket</seeerl>, wrong
+ For <seeerl marker="kernel:socket#">socket</seeerl>, wrong
type for protocol caused segmentation fault if protocol
was provided as {raw, integer()}.</p>
<p>
@@ -437,7 +437,7 @@
</item>
<item>
<p>
- For <seeerl marker="socket#">socket</seeerl>, when
+ For <seeerl marker="kernel:socket#">socket</seeerl>, when
setting the ip option 'recvttl' to true, thereby
indicating that we want to receive the TTL control
message header, we don't actually get TTL but RECVTTL on
@@ -448,7 +448,7 @@
</item>
<item>
<p>
- For <seeerl marker="socket#">socket</seeerl>, IPv6
+ For <seeerl marker="kernel:socket#">socket</seeerl>, IPv6
control message headers was incorrectly decoded with
level ip instead of ipv6.</p>
<p>
@@ -494,7 +494,7 @@
</item>
<item>
<p>
- The <seeerl marker="socket#">socket</seeerl> socket
+ The <seeerl marker="kernel:socket#">socket</seeerl> socket
option 'peek_off' has been disabled. If peek_off was set
and then socket:recv/3 was called with the peek flag, the
call could hang.</p>
@@ -544,7 +544,7 @@
</item>
<item>
<p>
- For <seeerl marker="socket#">socket</seeerl>, the
+ For <seeerl marker="kernel:socket#">socket</seeerl>, the
timestamp creation used when printing warning messages
and debug printouts did not work. The used buffer was too
small.</p>
@@ -590,7 +590,7 @@
<list>
<item>
<p>
- For <seeerl marker="socket#">socket</seeerl>, not all
+ For <seeerl marker="kernel:socket#">socket</seeerl>, not all
send and receive flags are supported on all platforms. In
order to (at least) simplify testing, the
socket:supports/0,1,2,3 functions has been extended with
@@ -601,7 +601,7 @@
</item>
<item>
<p>
- For <seeerl marker="socket#">socket</seeerl>, add
+ For <seeerl marker="kernel:socket#">socket</seeerl>, add
support for IPv6 socket options tclass and recvtclass.
Both has been added, but the use of them are platform
dependent. Call socket:supports(options, ipv6, Opt) to be
@@ -612,7 +612,7 @@
</item>
<item>
<p>
- For <seeerl marker="socket#">socket</seeerl>, the TCP
+ For <seeerl marker="kernel:socket#">socket</seeerl>, the TCP
socket option cork was not supported even though the
supports function reported it as such.</p>
<p>
@@ -645,7 +645,7 @@
</item>
<item>
<p>
- For <seeerl marker="socket#">socket</seeerl>, added
+ For <seeerl marker="kernel:socket#">socket</seeerl>, added
support for the socket option extended_err. Andreas
Schultz.</p>
<p>
diff --git a/erts/doc/src/part.xml.src b/erts/doc/src/part.xml
index 9b20beffad..5c5e1bedb4 100644
--- a/erts/doc/src/part.xml.src
+++ b/erts/doc/src/part.xml
@@ -42,7 +42,6 @@
<xi:include href="tty.xml"/>
<xi:include href="driver.xml"/>
<xi:include href="inet_cfg.xml"/>
- %ESOCK_USE_SOCKET_USAGE_XML%
<xi:include href="erl_ext_dist.xml"/>
<xi:include href="erl_dist_protocol.xml"/>
</part>
diff --git a/erts/doc/src/ref_man.xml.src b/erts/doc/src/ref_man.xml
index 5b327165d4..f78d6549a8 100644
--- a/erts/doc/src/ref_man.xml.src
+++ b/erts/doc/src/ref_man.xml
@@ -48,7 +48,6 @@
<xi:include href="init.xml"/>
<xi:include href="persistent_term.xml"/>
<xi:include href="run_erl_cmd.xml"/>
- %ESOCK_USE_SOCKET_XML%
<xi:include href="start_cmd.xml"/>
<xi:include href="start_erl_cmd.xml"/>
<xi:include href="werl_cmd.xml"/>
diff --git a/erts/doc/src/specs.xml.src b/erts/doc/src/specs.xml
index 54224c15f5..0b943e6295 100644
--- a/erts/doc/src/specs.xml.src
+++ b/erts/doc/src/specs.xml
@@ -5,7 +5,6 @@
<xi:include href="../specs/specs_erl_tracer.xml"/>
<xi:include href="../specs/specs_init.xml"/>
<xi:include href="../specs/specs_persistent_term.xml"/>
- %ESOCK_USE_SOCKET_SPECS_XML%
<xi:include href="../specs/specs_zlib.xml"/>
<xi:include href="../specs/specs_atomics.xml"/>
<xi:include href="../specs/specs_counters.xml"/>
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index 275b489bb0..6760207fa8 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -662,7 +662,7 @@ GENERATE += $(TARGET)/erl_db_insert_list.ycf.h
ifeq ($(USE_ESOCK), yes)
ESOCK_PRELOAD_BEAM = \
$(ERL_TOP)/erts/preloaded/ebin/socket_registry.beam \
- $(ERL_TOP)/erts/preloaded/ebin/socket.beam \
+ $(ERL_TOP)/erts/preloaded/ebin/prim_socket.beam \
$(ERL_TOP)/erts/preloaded/ebin/prim_net.beam
else
ESOCK_PRELOAD_BEAM =
@@ -875,7 +875,7 @@ ifeq ($(USE_ESOCK), yes)
# WE ARE USING ESOCK
ESOCK_NIF_OBJS = \
- $(OBJDIR)/socket_nif.o \
+ $(OBJDIR)/prim_socket_nif.o \
$(OBJDIR)/prim_net_nif.o
ifneq ($(TARGET), win32)
@@ -1222,8 +1222,7 @@ ifeq ($(TARGET), win32)
# These are *currently* only needed for non-win32,
# since the nif-functions for socket and net are basically
# stubbed with badarg in the win32 case.
-NIF_SOCKET_UTILS_SRC=$(filter-out nifs/common/socket_nif.c, $(wildcard nifs/common/socket_*.c))
-NIF_COMMON_SRC=$(filter-out $(NIF_SOCKET_UTILS_SRC), $(wildcard nifs/common/*.c))
+NIF_COMMON_SRC=$(filter-out $(wildcard nifs/common/socket_*.c), $(wildcard nifs/common/*.c))
else
NIF_COMMON_SRC=$(wildcard nifs/common/*.c)
endif
diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/prim_socket_nif.c
index c38a3f5511..cf01f710f5 100644
--- a/erts/emulator/nifs/common/socket_nif.c
+++ b/erts/emulator/nifs/common/prim_socket_nif.c
@@ -368,7 +368,7 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL;
#define ESOCK_GLOBAL_DEBUG_DEFAULT FALSE
#define ESOCK_DEBUG_DEFAULT FALSE
-/* Counters and stuff (Don't know where to sen2 this stuff anyway) */
+/* Counters and stuff (Don't know where to send this stuff anyway) */
#define ESOCK_NIF_IOW_DEFAULT FALSE
@@ -445,23 +445,6 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL;
#define ESOCK_GET_RESOURCE(ENV, REF, RES) \
enif_get_resource((ENV), (REF), esocks, (RES))
-#define ESOCK_SEND_FLAG_CONFIRM 0
-#define ESOCK_SEND_FLAG_DONTROUTE 1
-#define ESOCK_SEND_FLAG_EOR 2
-#define ESOCK_SEND_FLAG_MORE 3
-#define ESOCK_SEND_FLAG_NOSIGNAL 4
-#define ESOCK_SEND_FLAG_OOB 5
-#define ESOCK_SEND_FLAG_LOW ESOCK_SEND_FLAG_CONFIRM
-#define ESOCK_SEND_FLAG_HIGH ESOCK_SEND_FLAG_OOB
-
-#define ESOCK_RECV_FLAG_CMSG_CLOEXEC 0
-#define ESOCK_RECV_FLAG_ERRQUEUE 1
-#define ESOCK_RECV_FLAG_OOB 2
-#define ESOCK_RECV_FLAG_PEEK 3
-#define ESOCK_RECV_FLAG_TRUNC 4
-#define ESOCK_RECV_FLAG_LOW ESOCK_RECV_FLAG_CMSG_CLOEXEC
-#define ESOCK_RECV_FLAG_HIGH ESOCK_RECV_FLAG_TRUNC
-
#define ESOCK_RECV_BUFFER_COUNT_DEFAULT 0
#define ESOCK_RECV_BUFFER_SIZE_DEFAULT 8192
#define ESOCK_RECV_CTRL_BUFFER_SIZE_DEFAULT 1024
@@ -504,26 +487,23 @@ typedef union {
* This section must be "identical" to the corresponding socket.erl
*/
-/* domain */
-#define ESOCK_DOMAIN_LOCAL 1
-#define ESOCK_DOMAIN_INET 2
-#define ESOCK_DOMAIN_INET6 3
+#define ESOCK_SEND_FLAG_CONFIRM (1 << 0)
+#define ESOCK_SEND_FLAG_DONTROUTE (1 << 1)
+#define ESOCK_SEND_FLAG_EOR (1 << 2)
+#define ESOCK_SEND_FLAG_MORE (1 << 3)
+#define ESOCK_SEND_FLAG_NOSIGNAL (1 << 4)
+#define ESOCK_SEND_FLAG_OOB (1 << 5)
+/**/
+#define ESOCK_SEND_FLAG_MASK ((1 << 6) - 1)
-/* type */
-#define ESOCK_TYPE_STREAM 1
-#define ESOCK_TYPE_DGRAM 2
-#define ESOCK_TYPE_RAW 3
-// #define ESOCK_TYPE_RDM 4
-#define ESOCK_TYPE_SEQPACKET 5
+#define ESOCK_RECV_FLAG_CMSG_CLOEXEC (1 << 0)
+#define ESOCK_RECV_FLAG_ERRQUEUE (1 << 1)
+#define ESOCK_RECV_FLAG_OOB (1 << 2)
+#define ESOCK_RECV_FLAG_PEEK (1 << 3)
+#define ESOCK_RECV_FLAG_TRUNC (1 << 4)
+/**/
+#define ESOCK_RECV_FLAG_MASK ((1 << 5) - 1)
-/* protocol */
-#define ESOCK_PROTOCOL_DEFAULT 0
-#define ESOCK_PROTOCOL_IP 1
-#define ESOCK_PROTOCOL_TCP 2
-#define ESOCK_PROTOCOL_UDP 3
-#define ESOCK_PROTOCOL_SCTP 4
-#define ESOCK_PROTOCOL_ICMP 5
-#define ESOCK_PROTOCOL_IGMP 6
/* shutdown how */
#define ESOCK_SHUTDOWN_HOW_RD 0
@@ -531,121 +511,212 @@ typedef union {
#define ESOCK_SHUTDOWN_HOW_RDWR 2
-#define ESOCK_OPT_LEVEL_OTP 0
-#define ESOCK_OPT_LEVEL_SOCKET 1
-#define ESOCK_OPT_LEVEL_IP 2
-#define ESOCK_OPT_LEVEL_IPV6 3
-#define ESOCK_OPT_LEVEL_TCP 4
-#define ESOCK_OPT_LEVEL_UDP 5
-#define ESOCK_OPT_LEVEL_SCTP 6
-
-#define ESOCK_OPT_OTP_DEBUG 1
-#define ESOCK_OPT_OTP_IOW 2
-#define ESOCK_OPT_OTP_CTRL_PROC 3
-#define ESOCK_OPT_OTP_RCVBUF 4
-#define ESOCK_OPT_OTP_RCVCTRLBUF 6
-#define ESOCK_OPT_OTP_SNDCTRLBUF 7
-#define ESOCK_OPT_OTP_FD 8
-#define ESOCK_OPT_OTP_META 9
-#define ESOCK_OPT_OTP_DOMAIN 0xFF01 // INTERNAL AND ONLY GET
-#define ESOCK_OPT_OTP_TYPE 0xFF02 // INTERNAL AND ONLY GET
-#define ESOCK_OPT_OTP_PROTOCOL 0xFF03 // INTERNAL AND ONLY GET
-#define ESOCK_OPT_OTP_DTP 0xFF04 // INTERNAL AND ONLY GET
-
-#define ESOCK_OPT_SOCK_ACCEPTCONN 1
-#define ESOCK_OPT_SOCK_BINDTODEVICE 3
-#define ESOCK_OPT_SOCK_BROADCAST 4
-#define ESOCK_OPT_SOCK_DEBUG 6
-#define ESOCK_OPT_SOCK_DOMAIN 7
-#define ESOCK_OPT_SOCK_DONTROUTE 8
-#define ESOCK_OPT_SOCK_KEEPALIVE 10
-#define ESOCK_OPT_SOCK_LINGER 11
-#define ESOCK_OPT_SOCK_OOBINLINE 13
-#define ESOCK_OPT_SOCK_PASSCRED 14
-#define ESOCK_OPT_SOCK_PEEK_OFF 15
-#define ESOCK_OPT_SOCK_PRIORITY 17
-#define ESOCK_OPT_SOCK_PROTOCOL 18
-#define ESOCK_OPT_SOCK_RCVBUF 19
-#define ESOCK_OPT_SOCK_RCVLOWAT 21
-#define ESOCK_OPT_SOCK_RCVTIMEO 22
-#define ESOCK_OPT_SOCK_REUSEADDR 23
-#define ESOCK_OPT_SOCK_REUSEPORT 24
-#define ESOCK_OPT_SOCK_SNDBUF 27
-#define ESOCK_OPT_SOCK_SNDLOWAT 29
-#define ESOCK_OPT_SOCK_SNDTIMEO 30
-#define ESOCK_OPT_SOCK_TIMESTAMP 31
-#define ESOCK_OPT_SOCK_TYPE 32
-
-#define ESOCK_OPT_IP_ADD_MEMBERSHIP 1
-#define ESOCK_OPT_IP_ADD_SOURCE_MEMBERSHIP 2
-#define ESOCK_OPT_IP_BLOCK_SOURCE 3
-#define ESOCK_OPT_IP_DROP_MEMBERSHIP 5
-#define ESOCK_OPT_IP_DROP_SOURCE_MEMBERSHIP 6
-#define ESOCK_OPT_IP_FREEBIND 7
-#define ESOCK_OPT_IP_HDRINCL 8
-#define ESOCK_OPT_IP_MINTTL 9
-#define ESOCK_OPT_IP_MSFILTER 10
-#define ESOCK_OPT_IP_MTU 11
-#define ESOCK_OPT_IP_MTU_DISCOVER 12
-#define ESOCK_OPT_IP_MULTICAST_ALL 13
-#define ESOCK_OPT_IP_MULTICAST_IF 14
-#define ESOCK_OPT_IP_MULTICAST_LOOP 15
-#define ESOCK_OPT_IP_MULTICAST_TTL 16
-#define ESOCK_OPT_IP_NODEFRAG 17
-#define ESOCK_OPT_IP_PKTINFO 19
-#define ESOCK_OPT_IP_RECVDSTADDR 20
-#define ESOCK_OPT_IP_RECVERR 21
-#define ESOCK_OPT_IP_RECVIF 22
-#define ESOCK_OPT_IP_RECVOPTS 23
-#define ESOCK_OPT_IP_RECVORIGDSTADDR 24
-#define ESOCK_OPT_IP_RECVTOS 25
-#define ESOCK_OPT_IP_RECVTTL 26
-#define ESOCK_OPT_IP_RETOPTS 27
-#define ESOCK_OPT_IP_ROUTER_ALERT 28
-#define ESOCK_OPT_IP_SENDSRCADDR 29 // Same as IP_RECVDSTADDR?
-#define ESOCK_OPT_IP_TOS 30
-#define ESOCK_OPT_IP_TRANSPARENT 31
-#define ESOCK_OPT_IP_TTL 32
-#define ESOCK_OPT_IP_UNBLOCK_SOURCE 33
-
-#define ESOCK_OPT_IPV6_ADDRFORM 1
-#define ESOCK_OPT_IPV6_ADD_MEMBERSHIP 2
-#define ESOCK_OPT_IPV6_AUTHHDR 3
-#define ESOCK_OPT_IPV6_DROP_MEMBERSHIP 6
-#define ESOCK_OPT_IPV6_DSTOPTS 7
-#define ESOCK_OPT_IPV6_FLOWINFO 11
-#define ESOCK_OPT_IPV6_HOPLIMIT 12
-#define ESOCK_OPT_IPV6_HOPOPTS 13
-#define ESOCK_OPT_IPV6_MTU 17
-#define ESOCK_OPT_IPV6_MTU_DISCOVER 18
-#define ESOCK_OPT_IPV6_MULTICAST_HOPS 19
-#define ESOCK_OPT_IPV6_MULTICAST_IF 20
-#define ESOCK_OPT_IPV6_MULTICAST_LOOP 21
-#define ESOCK_OPT_IPV6_RECVERR 24
-#define ESOCK_OPT_IPV6_RECVHOPLIMIT 25
-#define ESOCK_OPT_IPV6_RECVPKTINFO 26 // PKTINFO on FreeBSD
-#define ESOCK_OPT_IPV6_RECVTCLASS 27 // Linux and ?
-#define ESOCK_OPT_IPV6_ROUTER_ALERT 28
-#define ESOCK_OPT_IPV6_RTHDR 29
-#define ESOCK_OPT_IPV6_TCLASS 30
-#define ESOCK_OPT_IPV6_UNICAST_HOPS 31
-#define ESOCK_OPT_IPV6_V6ONLY 33
-
-#define ESOCK_OPT_TCP_CONGESTION 1
-#define ESOCK_OPT_TCP_CORK 2
-#define ESOCK_OPT_TCP_MAXSEG 7
-#define ESOCK_OPT_TCP_NODELAY 9
-
-#define ESOCK_OPT_UDP_CORK 1
-
-#define ESOCK_OPT_SCTP_ASSOCINFO 2
-#define ESOCK_OPT_SCTP_AUTOCLOSE 8
-#define ESOCK_OPT_SCTP_DISABLE_FRAGMENTS 12
-#define ESOCK_OPT_SCTP_EVENTS 14
-#define ESOCK_OPT_SCTP_INITMSG 18
-#define ESOCK_OPT_SCTP_MAXSEG 21
-#define ESOCK_OPT_SCTP_NODELAY 23
-#define ESOCK_OPT_SCTP_RTOINFO 29
+
+/* domain */
+#define ESOCK_DOMAIN_LOCAL 1
+#define ESOCK_DOMAIN_INET 2
+#define ESOCK_DOMAIN_INET6 3
+
+/* type */
+#define ESOCK_TYPE_STREAM 101
+#define ESOCK_TYPE_DGRAM 102
+#define ESOCK_TYPE_RAW 103
+// #define ESOCK_TYPE_RDM 104
+#define ESOCK_TYPE_SEQPACKET 105
+
+/* protocol */
+#define ESOCK_PROTOCOL_DEFAULT 200
+#define ESOCK_PROTOCOL_IP 201
+#define ESOCK_PROTOCOL_TCP 202
+#define ESOCK_PROTOCOL_UDP 203
+#define ESOCK_PROTOCOL_SCTP 204
+#define ESOCK_PROTOCOL_ICMP 205
+#define ESOCK_PROTOCOL_IGMP 206
+
+/* option level */
+#define ESOCK_OPT_LEVEL_OTP 301
+#define ESOCK_OPT_LEVEL_SOCKET 302
+#define ESOCK_OPT_LEVEL_IP 303
+#define ESOCK_OPT_LEVEL_IPV6 304
+#define ESOCK_OPT_LEVEL_TCP 305
+#define ESOCK_OPT_LEVEL_UDP 306
+#define ESOCK_OPT_LEVEL_SCTP 307
+
+/* level 'otp' options */
+#define ESOCK_OPT_OTP_DEBUG 1001
+#define ESOCK_OPT_OTP_IOW 1002
+#define ESOCK_OPT_OTP_CTRL_PROC 1003
+#define ESOCK_OPT_OTP_RCVBUF 1004
+//#define ESOCK_OPT_OTP_SNDBUF 1005
+#define ESOCK_OPT_OTP_RCVCTRLBUF 1006
+#define ESOCK_OPT_OTP_SNDCTRLBUF 1007
+#define ESOCK_OPT_OTP_FD 1008
+#define ESOCK_OPT_OTP_META 1009
+/**/
+#define ESOCK_OPT_OTP_DOMAIN 1999 // INTERNAL AND ONLY GET
+#define ESOCK_OPT_OTP_TYPE 1998 // INTERNAL AND ONLY GET
+#define ESOCK_OPT_OTP_PROTOCOL 1997 // INTERNAL AND ONLY GET
+#define ESOCK_OPT_OTP_DTP 1996 // INTERNAL AND ONLY GET
+
+/* level 'socket' options */
+#define ESOCK_OPT_SOCK_ACCEPTCONN 2001
+//#define ESOCK_OPT_SOCK_ACCEPTFILTER 2002
+#define ESOCK_OPT_SOCK_BINDTODEVICE 2003
+#define ESOCK_OPT_SOCK_BROADCAST 2004
+//#define ESOCK_OPT_SOCK_BUSY_POLL 2005
+#define ESOCK_OPT_SOCK_DEBUG 2006
+#define ESOCK_OPT_SOCK_DOMAIN 2007
+#define ESOCK_OPT_SOCK_DONTROUTE 2008
+//#define ESOCK_OPT_SOCK_ERROR 2009
+#define ESOCK_OPT_SOCK_KEEPALIVE 2010
+#define ESOCK_OPT_SOCK_LINGER 2011
+//#define ESOCK_OPT_SOCK_MARK 2012
+#define ESOCK_OPT_SOCK_OOBINLINE 2013
+#define ESOCK_OPT_SOCK_PASSCRED 2014
+#define ESOCK_OPT_SOCK_PEEK_OFF 2015
+//#define ESOCK_OPT_SOCK_PEERCRED 2016
+#define ESOCK_OPT_SOCK_PRIORITY 2017
+#define ESOCK_OPT_SOCK_PROTOCOL 2018
+#define ESOCK_OPT_SOCK_RCVBUF 2019
+//#define ESOCK_OPT_SOCK_RCVBUFFORCE 2020
+#define ESOCK_OPT_SOCK_RCVLOWAT 2021
+#define ESOCK_OPT_SOCK_RCVTIMEO 2022
+#define ESOCK_OPT_SOCK_REUSEADDR 2023
+#define ESOCK_OPT_SOCK_REUSEPORT 2024
+//#define ESOCK_OPT_SOCK_RXQ_OVFL 2025
+//#define ESOCK_OPT_SOCK_SETFIB 2026
+#define ESOCK_OPT_SOCK_SNDBUF 2027
+//#define ESOCK_OPT_SOCK_SNDBUFFORCE 2028
+#define ESOCK_OPT_SOCK_SNDLOWAT 2029
+#define ESOCK_OPT_SOCK_SNDTIMEO 2030
+#define ESOCK_OPT_SOCK_TIMESTAMP 2031
+#define ESOCK_OPT_SOCK_TYPE 2032
+
+/* level 'ip' options */
+#define ESOCK_OPT_IP_ADD_MEMBERSHIP 3001
+#define ESOCK_OPT_IP_ADD_SOURCE_MEMBERSHIP 3002
+#define ESOCK_OPT_IP_BLOCK_SOURCE 3003
+//#define ESOCK_OPT_IP_DONTFRAG 3004
+#define ESOCK_OPT_IP_DROP_MEMBERSHIP 3005
+#define ESOCK_OPT_IP_DROP_SOURCE_MEMBERSHIP 3006
+#define ESOCK_OPT_IP_FREEBIND 3007
+#define ESOCK_OPT_IP_HDRINCL 3008
+#define ESOCK_OPT_IP_MINTTL 3009
+#define ESOCK_OPT_IP_MSFILTER 3010
+#define ESOCK_OPT_IP_MTU 3011
+#define ESOCK_OPT_IP_MTU_DISCOVER 3012
+#define ESOCK_OPT_IP_MULTICAST_ALL 3013
+#define ESOCK_OPT_IP_MULTICAST_IF 3014
+#define ESOCK_OPT_IP_MULTICAST_LOOP 3015
+#define ESOCK_OPT_IP_MULTICAST_TTL 3016
+#define ESOCK_OPT_IP_NODEFRAG 3017
+//#define ESOCK_OPT_IP_OPTIONS 3018
+#define ESOCK_OPT_IP_PKTINFO 3019
+#define ESOCK_OPT_IP_RECVDSTADDR 3020
+#define ESOCK_OPT_IP_RECVERR 3021
+#define ESOCK_OPT_IP_RECVIF 3022
+#define ESOCK_OPT_IP_RECVOPTS 3023
+#define ESOCK_OPT_IP_RECVORIGDSTADDR 3024
+#define ESOCK_OPT_IP_RECVTOS 3025
+#define ESOCK_OPT_IP_RECVTTL 3026
+#define ESOCK_OPT_IP_RETOPTS 3027
+#define ESOCK_OPT_IP_ROUTER_ALERT 3028
+#define ESOCK_OPT_IP_SENDSRCADDR 3029 // Same as IP_RECVDSTADDR?
+#define ESOCK_OPT_IP_TOS 3030
+#define ESOCK_OPT_IP_TRANSPARENT 3031
+#define ESOCK_OPT_IP_TTL 3032
+#define ESOCK_OPT_IP_UNBLOCK_SOURCE 3033
+
+/* level 'ipv6' options */
+#define ESOCK_OPT_IPV6_ADDRFORM 4001
+#define ESOCK_OPT_IPV6_ADD_MEMBERSHIP 4002
+#define ESOCK_OPT_IPV6_AUTHHDR 4003
+//#define ESOCK_OPT_IPV6_AUTH_LEVEL 4004
+//#define ESOCK_OPT_IPV6_CHECKSUM 4005
+#define ESOCK_OPT_IPV6_DROP_MEMBERSHIP 4006
+#define ESOCK_OPT_IPV6_DSTOPTS 4007
+//#define ESOCK_OPT_IPV6_ESP_NETWORK_LEVEL 4008
+//#define ESOCK_OPT_IPV6_ESP_TRANS_LEVEL 4009
+//#define ESOCK_OPT_IPV6_FAITH 4010
+#define ESOCK_OPT_IPV6_FLOWINFO 4011
+#define ESOCK_OPT_IPV6_HOPLIMIT 4012
+#define ESOCK_OPT_IPV6_HOPOPTS 4013
+//#define ESOCK_OPT_IPV6_IPCOMP_LEVEL 4014
+//#define ESOCK_OPT_IPV6_JOIN_GROUP 4015
+//#define ESOCK_OPT_IPV6_LEAVE_GROUP 4016
+#define ESOCK_OPT_IPV6_MTU 4017
+#define ESOCK_OPT_IPV6_MTU_DISCOVER 4018
+#define ESOCK_OPT_IPV6_MULTICAST_HOPS 4019
+#define ESOCK_OPT_IPV6_MULTICAST_IF 4020
+#define ESOCK_OPT_IPV6_MULTICAST_LOOP 4021
+//#define ESOCK_OPT_IPV6_PORTRANGE 4022
+//#define ESOCK_OPT_IPV6_PKTOPTIONS 4023
+#define ESOCK_OPT_IPV6_RECVERR 4024
+#define ESOCK_OPT_IPV6_RECVHOPLIMIT 4025
+#define ESOCK_OPT_IPV6_RECVPKTINFO 4026 // PKTINFO on FreeBSD
+#define ESOCK_OPT_IPV6_RECVTCLASS 4027 // Linux and ?
+#define ESOCK_OPT_IPV6_ROUTER_ALERT 4028
+#define ESOCK_OPT_IPV6_RTHDR 4029
+#define ESOCK_OPT_IPV6_TCLASS 4030
+#define ESOCK_OPT_IPV6_UNICAST_HOPS 4031
+//#define ESOCK_OPT_IPV6_USE_MIN_MTU 4032
+#define ESOCK_OPT_IPV6_V6ONLY 4033
+
+/* level 'tcp' options */
+#define ESOCK_OPT_TCP_CONGESTION 5001
+#define ESOCK_OPT_TCP_CORK 5002
+//#define ESOCK_OPT_TCP_INFO 5003
+//#define ESOCK_OPT_TCP_KEEPCNT 5004
+//#define ESOCK_OPT_TCP_KEEPIDLE 5005
+//#define ESOCK_OPT_TCP_KEEPINTVL 5006
+#define ESOCK_OPT_TCP_MAXSEG 5007
+//#define ESOCK_OPT_TCP_MD5SIG 5008
+#define ESOCK_OPT_TCP_NODELAY 5009
+//#define ESOCK_OPT_TCP_NOOPT 5010
+//#define ESOCK_OPT_TCP_NOPUSH 5011
+//#define ESOCK_OPT_TCP_SYNCNT 5012
+//#define ESOCK_OPT_TCP_USER_TIMEOUT 5013
+
+/* level 'udp' options */
+#define ESOCK_OPT_UDP_CORK 6001
+
+/* level 'sctp' options */
+//#define ESOCK_OPT_SCTP_ADAPTION_LAYER 7001
+#define ESOCK_OPT_SCTP_ASSOCINFO 7002
+//#define ESOCK_OPT_SCTP_AUTH_ACTIVE_KEY 7003
+//#define ESOCK_OPT_SCTP_AUTH_ASCONF 7004
+//#define ESOCK_OPT_SCTP_AUTH_CHUNK 7005
+//#define ESOCK_OPT_SCTP_AUTH_KEY 7006
+//#define ESOCK_OPT_SCTP_AUTH_DELETE_KEY 7007
+#define ESOCK_OPT_SCTP_AUTOCLOSE 7008
+//#define ESOCK_OPT_SCTP_CONTEXT 7009
+//#define ESOCK_OPT_SCTP_DEFAULT_SEND_PARAMS 7010
+//#define ESOCK_OPT_SCTP_DELAYED_ACK_TIME 7011
+#define ESOCK_OPT_SCTP_DISABLE_FRAGMENTS 7012
+//#define ESOCK_OPT_SCTP_HMAC_IDENT 7013
+#define ESOCK_OPT_SCTP_EVENTS 7014
+//#define ESOCK_OPT_SCTP_EXPLICIT_EOR 7015
+//#define ESOCK_OPT_SCTP_FRAGMENT_INTERLEAVE 7016
+//#define ESOCK_OPT_SCTP_GET_PEER_ADDR_INFO 7017
+#define ESOCK_OPT_SCTP_INITMSG 7018
+//#define ESOCK_OPT_SCTP_I_WANT_MAPPED_V4_ADDR 7019
+//#define ESOCK_OPT_SCTP_LOCAL_AUTH_CHUNKS 7020
+#define ESOCK_OPT_SCTP_MAXSEG 7021
+//#define ESOCK_OPT_SCTP_MAXBURST 7022
+#define ESOCK_OPT_SCTP_NODELAY 7023
+//#define ESOCK_OPT_SCTP_PARTIAL_DELIVERY_POINT 7024
+//#define ESOCK_OPT_SCTP_PEER_ADDR_PARAMS 7025
+//#define ESOCK_OPT_SCTP_PEER_AUTH_CHUNKS 7026
+//#define ESOCK_OPT_SCTP_PRIMARY_ADDR 7027
+//#define ESOCK_OPT_SCTP_RESET_STREAMS 7028
+#define ESOCK_OPT_SCTP_RTOINFO 7029
+//#define ESOCK_OPT_SCTP_SET_PEER_PRIMARY_ADDR 7030
+//#define ESOCK_OPT_SCTP_STATUS 7031
+//#define ESOCK_OPT_SCTP_USE_EXT_RECVINFO 7032
+
+
+/*--------------------------------------------------------------------------*/
+
/* We should *eventually* use this instead of hard-coding the size (to 1) */
#define ESOCK_RECVMSG_IOVEC_SZ 1
@@ -653,14 +724,6 @@ typedef union {
#define ESOCK_CMD_DEBUG 0x0001
#define ESOCK_CMD_SOCKET_DEBUG 0x0002
-#define ESOCK_SUPPORTS_OPTIONS 0x0001
-#define ESOCK_SUPPORTS_SCTP 0x0002
-#define ESOCK_SUPPORTS_IPV6 0x0003
-#define ESOCK_SUPPORTS_LOCAL 0x0004
-#define ESOCK_SUPPORTS_SEND_FLAGS 0x0005
-#define ESOCK_SUPPORTS_RECV_FLAGS 0x0006
-#define ESOCK_SUPPORTS_NETNS 0x0007
-
#define ESOCK_WHICH_DOMAIN_ERROR -1
#define ESOCK_WHICH_DOMAIN_UNSUP -2
#define ESOCK_WHICH_TYPE_ERROR -1
@@ -677,9 +740,9 @@ typedef union {
/* Global socket debug */
#define SGDBG( proto ) ESOCK_DBG_PRINTF( data.dbg , proto )
-#define SGDBG2( __DBG__ , proto ) ESOCK_DBG_PRINTF( __DBG__ || data.dbg , proto )
/* Socket specific debug */
#define SSDBG( __D__ , proto ) ESOCK_DBG_PRINTF( (__D__)->dbg , proto )
+#define SSDBG2( __DBG__ , proto ) ESOCK_DBG_PRINTF( (__DBG__) , proto )
#define ESOCK_CNT_INC( __E__, __D__, SF, ACNT, CNT, INC) \
{ \
@@ -1058,7 +1121,6 @@ extern char* erl_errno_id(int error); /* THIS IS JUST TEMPORARY??? */
* nif_getopt
* nif_sockname
* nif_peername
- * nif_finalize_connection
* nif_finalize_close
* nif_cancel
*/
@@ -1084,7 +1146,6 @@ extern char* erl_errno_id(int error); /* THIS IS JUST TEMPORARY??? */
ESOCK_NIF_FUNC_DEF(getopt); \
ESOCK_NIF_FUNC_DEF(sockname); \
ESOCK_NIF_FUNC_DEF(peername); \
- ESOCK_NIF_FUNC_DEF(finalize_connection); \
ESOCK_NIF_FUNC_DEF(finalize_close); \
ESOCK_NIF_FUNC_DEF(cancel);
@@ -1139,26 +1200,27 @@ static ERL_NIF_TERM socket_info_reqs(ErlNifEnv* env,
ESockRequestor* crp,
ESockRequestQueue* q);
-static ERL_NIF_TERM esock_supports(ErlNifEnv* env, int key);
-static ERL_NIF_TERM esock_supports_options(ErlNifEnv* env);
+static ERL_NIF_TERM esock_supports_0(ErlNifEnv* env);
+static ERL_NIF_TERM esock_supports_1(ErlNifEnv* env, ERL_NIF_TERM key);
+static ERL_NIF_TERM esock_supports_2(ErlNifEnv* env,
+ ERL_NIF_TERM key1, int key2);
+//static ERL_NIF_TERM esock_supports_options(ErlNifEnv* env);
static ERL_NIF_TERM esock_supports_options_socket(ErlNifEnv* env);
static ERL_NIF_TERM esock_supports_options_ip(ErlNifEnv* env);
static ERL_NIF_TERM esock_supports_options_ipv6(ErlNifEnv* env);
static ERL_NIF_TERM esock_supports_options_tcp(ErlNifEnv* env);
static ERL_NIF_TERM esock_supports_options_udp(ErlNifEnv* env);
static ERL_NIF_TERM esock_supports_options_sctp(ErlNifEnv* env);
-static ERL_NIF_TERM esock_supports_sctp(ErlNifEnv* env);
-static ERL_NIF_TERM esock_supports_ipv6(ErlNifEnv* env);
-static ERL_NIF_TERM esock_supports_local(ErlNifEnv* env);
-static ERL_NIF_TERM esock_supports_netns(ErlNifEnv* env);
+//static ERL_NIF_TERM esock_supports_sctp(ErlNifEnv* env);
+//static ERL_NIF_TERM esock_supports_ipv6(ErlNifEnv* env);
+//static ERL_NIF_TERM esock_supports_local(ErlNifEnv* env);
+//static ERL_NIF_TERM esock_supports_netns(ErlNifEnv* env);
static ERL_NIF_TERM esock_supports_send_flags(ErlNifEnv* env);
static ERL_NIF_TERM esock_supports_recv_flags(ErlNifEnv* env);
static ERL_NIF_TERM esock_open2(ErlNifEnv* env,
int fd,
ERL_NIF_TERM eextra);
-static BOOLEAN_T esock_open2_is_debug(ErlNifEnv* env,
- ERL_NIF_TERM eextra);
static BOOLEAN_T esock_open2_todup(ErlNifEnv* env,
ERL_NIF_TERM eextra);
static BOOLEAN_T esock_open2_get_domain(ErlNifEnv* env,
@@ -1175,6 +1237,9 @@ static ERL_NIF_TERM esock_open4(ErlNifEnv* env,
int type,
int protocol,
ERL_NIF_TERM eopts);
+static BOOLEAN_T esock_open_is_debug(ErlNifEnv* env,
+ ERL_NIF_TERM eextra,
+ BOOLEAN_T dflt);
static BOOLEAN_T esock_open_which_domain(SOCKET sock, int* domain);
static BOOLEAN_T esock_open_which_type(SOCKET sock, int* type);
static BOOLEAN_T esock_open_which_protocol(SOCKET sock, int* proto);
@@ -2443,8 +2508,6 @@ static ERL_NIF_TERM recvmsg_check_msg(ErlNifEnv* env,
ErlNifBinary* ctrlBufP,
ERL_NIF_TERM sockRef);
-static ERL_NIF_TERM esock_finalize_connection(ErlNifEnv* env,
- ESockDescriptor* descP);
static ERL_NIF_TERM esock_finalize_close(ErlNifEnv* env,
ESockDescriptor* descP);
static int esock_close_socket(ErlNifEnv* env,
@@ -2862,7 +2925,7 @@ static const struct in6_addr in6addr_loopback =
/* (special) error string constants */
-static char str_exmon[] = "exmonitor"; // failed monitor
+static char str_exmonitor[] = "exmonitor"; // failed monitor
static char str_exself[] = "exself"; // failed self
static char str_exsend[] = "exsend"; // failed send
@@ -3134,6 +3197,7 @@ ERL_NIF_TERM esock_atom_socket_tag; // This has a "special" name ('$socket')
LOCAL_ATOM_DECL(dest_unreach); \
LOCAL_ATOM_DECL(do); \
LOCAL_ATOM_DECL(dont); \
+ LOCAL_ATOM_DECL(dup); \
LOCAL_ATOM_DECL(exclude); \
LOCAL_ATOM_DECL(false); \
LOCAL_ATOM_DECL(frag_needed); \
@@ -3181,6 +3245,7 @@ ERL_NIF_TERM esock_atom_socket_tag; // This has a "special" name ('$socket')
LOCAL_ATOM_DECL(num_tstreams); \
LOCAL_ATOM_DECL(num_writers); \
LOCAL_ATOM_DECL(offender); \
+ LOCAL_ATOM_DECL(options); \
LOCAL_ATOM_DECL(origin); \
LOCAL_ATOM_DECL(partial_delivery); \
LOCAL_ATOM_DECL(peer_error); \
@@ -3195,12 +3260,14 @@ ERL_NIF_TERM esock_atom_socket_tag; // This has a "special" name ('$socket')
LOCAL_ATOM_DECL(read_pkg_max); \
LOCAL_ATOM_DECL(read_tries); \
LOCAL_ATOM_DECL(read_waits); \
+ LOCAL_ATOM_DECL(recv_flags); \
LOCAL_ATOM_DECL(registry); \
LOCAL_ATOM_DECL(reject_route); \
LOCAL_ATOM_DECL(remote); \
LOCAL_ATOM_DECL(select); \
LOCAL_ATOM_DECL(sender_dry); \
LOCAL_ATOM_DECL(send_failure); \
+ LOCAL_ATOM_DECL(send_flags); \
LOCAL_ATOM_DECL(shutdown); \
LOCAL_ATOM_DECL(slist); \
LOCAL_ATOM_DECL(socket_debug); \
@@ -3223,12 +3290,12 @@ ERL_NIF_TERM esock_atom_socket_tag; // This has a "special" name ('$socket')
#define LOCAL_ERROR_REASON_ATOMS \
LOCAL_ATOM_DECL(econnreset); \
LOCAL_ATOM_DECL(eisconn); \
- LOCAL_ATOM_DECL(enotclosing); \
LOCAL_ATOM_DECL(enotconn); \
+ \
LOCAL_ATOM_DECL(exalloc); \
LOCAL_ATOM_DECL(exbadstate); \
- LOCAL_ATOM_DECL(exbusy); \
- LOCAL_ATOM_DECL(exmon); \
+ LOCAL_ATOM_DECL(exmonitor); \
+ LOCAL_ATOM_DECL(exselect); \
LOCAL_ATOM_DECL(exself); \
LOCAL_ATOM_DECL(exsend)
@@ -3418,8 +3485,14 @@ ERL_NIF_TERM esock_global_info(ErlNifEnv* env)
sizeof(gcnt) / sizeof(ERL_NIF_TERM);
ERL_NIF_TERM
lgcnt = MKLA(env, gcnt, lenGCnt),
- keys[] = {esock_atom_debug, atom_iow, atom_counters},
- vals[] = {BOOL2ATOM(data.dbg), BOOL2ATOM(data.iow), lgcnt},
+ keys[] = {esock_atom_debug,
+ atom_socket_debug,
+ atom_iow,
+ atom_counters},
+ vals[] = {BOOL2ATOM(data.dbg),
+ BOOL2ATOM(data.sockDbg),
+ BOOL2ATOM(data.iow),
+ lgcnt},
info;
unsigned int
numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM),
@@ -3461,12 +3534,13 @@ ERL_NIF_TERM esock_socket_info(ErlNifEnv* env,
ERL_NIF_TERM readers = esock_socket_info_readers(env, descP);
ERL_NIF_TERM writers = esock_socket_info_writers(env, descP);
ERL_NIF_TERM acceptors = esock_socket_info_acceptors(env, descP);
+ BOOLEAN_T hasRemote = descP->addrLen > 0;
ERL_NIF_TERM remote;
- BOOLEAN_T isConnected = (descP->writeState & ESOCK_STATE_CONNECTED) != 0;
- if ((! isConnected) ||
+ if ((! hasRemote) ||
esock_encode_sockaddr(env, &descP->remote,
- descP->addrLen, &remote) != NULL)
+ descP->addrLen, &remote) != NULL) {
remote = esock_atom_undefined; // not used
+ }
{
ERL_NIF_TERM keys[]
@@ -3503,7 +3577,7 @@ ERL_NIF_TERM esock_socket_info(ErlNifEnv* env,
ESOCK_ASSERT( (numKeys == numVals) );
if (!MKMA(env, keys, vals,
- isConnected ? numKeys : numKeys - 1, &info))
+ hasRemote ? numKeys : numKeys - 1, &info))
return enif_make_badarg(env);
return info;
@@ -3882,101 +3956,141 @@ ERL_NIF_TERM nif_supports(ErlNifEnv* env,
#if defined(__WIN32__)
return enif_raise_exception(env, MKA(env, "notsup"));
#else
- int key;
+ int key2;
SGDBG( ("SOCKET", "nif_supports -> entry with %d args\r\n", argc) );
/* Extract arguments and perform preliminary validation */
- if ((argc != 1) ||
- !GET_INT(env, argv[0], &key)) {
+ if (argc == 0)
+ return esock_supports_0(env);
+
+ if (argc == 1)
+ return esock_supports_1(env, argv[0]);
+
+ if (! GET_INT(env, argv[1], &key2))
return enif_make_badarg(env);
- }
- return esock_supports(env, key);
+ if (argc == 2)
+ return esock_supports_2(env, argv[0], key2);
+
+ return enif_make_badarg(env);
#endif
}
-
-
-/* esock_supports - what features do we support
+/* esock_supports - what features do we support?
*
- * This is to prove information about what features actually
+ * This gives information about what features actually
* work on the current platform.
*/
#if !defined(__WIN32__)
+
static
-ERL_NIF_TERM esock_supports(ErlNifEnv* env, int key)
+ERL_NIF_TERM esock_supports_0(ErlNifEnv* env)
{
- ERL_NIF_TERM result;
+ SocketTArray opts = TARRAY_CREATE(8);
+ ERL_NIF_TERM is_supported, opts_list;
- SGDBG( ("SOCKET", "esock_supports -> entry with 0x%lX\r\n", key) );
+ SGDBG( ("SOCKET", "esock_supports_0 -> entry\r\n") );
- switch (key) {
- case ESOCK_SUPPORTS_OPTIONS:
- result = esock_supports_options(env);
- break;
+#if defined(HAVE_SCTP)
+ is_supported = esock_atom_true;
+#else
+ is_supported = esock_atom_false;
+#endif
+ TARRAY_ADD(opts, MKT2(env, esock_atom_sctp, is_supported));
+
+ /* Is this (test) really sufficient for testing if we support IPv6? */
+#if defined(HAVE_IPV6)
+ is_supported = esock_atom_true;
+#else
+ is_supported = esock_atom_false;
+#endif
+ TARRAY_ADD(opts, MKT2(env, esock_atom_ipv6, is_supported));
- case ESOCK_SUPPORTS_SCTP:
- result = esock_supports_sctp(env);
- break;
+#if defined(AF_LOCAL)
+ is_supported = esock_atom_true;
+#else
+ is_supported = esock_atom_false;
+#endif
+ TARRAY_ADD(opts, MKT2(env, esock_atom_local, is_supported));
- case ESOCK_SUPPORTS_IPV6:
- result = esock_supports_ipv6(env);
- break;
+#if defined(HAVE_SETNS)
+ is_supported = esock_atom_true;
+#else
+ is_supported = esock_atom_false;
+#endif
+ TARRAY_ADD(opts, MKT2(env, atom_netns, is_supported));
- case ESOCK_SUPPORTS_LOCAL:
- result = esock_supports_local(env);
- break;
+ TARRAY_TOLIST(opts, env, &opts_list);
+ return opts_list;
+}
- case ESOCK_SUPPORTS_NETNS:
- result = esock_supports_netns(env);
- break;
+static
+ERL_NIF_TERM esock_supports_1(ErlNifEnv* env, ERL_NIF_TERM key)
+{
+ ERL_NIF_TERM result;
- case ESOCK_SUPPORTS_SEND_FLAGS:
- result = esock_supports_send_flags(env);
- break;
+ SGDBG( ("SOCKET",
+ "esock_supports_2 -> entry"
+ "\r\n key: %T"
+ "\r\n", key) );
- case ESOCK_SUPPORTS_RECV_FLAGS:
+ if (COMPARE(key, atom_send_flags) == 0)
+ result = esock_supports_send_flags(env);
+ else if (COMPARE(key, atom_recv_flags) == 0)
result = esock_supports_recv_flags(env);
- break;
-
- default:
- result = esock_atom_false;
- break;
- }
+ else
+ result = MKEL(env);
return result;
}
-#endif
-
-#if !defined(__WIN32__)
static
-ERL_NIF_TERM esock_supports_options(ErlNifEnv* env)
-{
- ERL_NIF_TERM sockOpts = esock_supports_options_socket(env);
- ERL_NIF_TERM sockOptsT = MKT2(env, esock_atom_socket, sockOpts);
- ERL_NIF_TERM ipOpts = esock_supports_options_ip(env);
- ERL_NIF_TERM ipOptsT = MKT2(env, esock_atom_ip, ipOpts);
- ERL_NIF_TERM ipv6Opts = esock_supports_options_ipv6(env);
- ERL_NIF_TERM ipv6OptsT = MKT2(env, esock_atom_ipv6, ipv6Opts);
- ERL_NIF_TERM tcpOpts = esock_supports_options_tcp(env);
- ERL_NIF_TERM tcpOptsT = MKT2(env, esock_atom_tcp, tcpOpts);
- ERL_NIF_TERM udpOpts = esock_supports_options_udp(env);
- ERL_NIF_TERM udpOptsT = MKT2(env, esock_atom_udp, udpOpts);
- ERL_NIF_TERM sctpOpts = esock_supports_options_sctp(env);
- ERL_NIF_TERM sctpOptsT = MKT2(env, esock_atom_sctp, sctpOpts);
- ERL_NIF_TERM optsA[] = {sockOptsT,
- ipOptsT, ipv6OptsT,
- tcpOptsT, udpOptsT, sctpOptsT};
- unsigned int lenOptsA = sizeof(optsA) / sizeof(ERL_NIF_TERM);
- ERL_NIF_TERM optsL = MKLA(env, optsA, lenOptsA);
+ERL_NIF_TERM esock_supports_2(ErlNifEnv* env, ERL_NIF_TERM key1, int key2)
+{
+ ERL_NIF_TERM result;
- return optsL;
+ SGDBG( ("SOCKET",
+ "esock_supports_2 -> entry"
+ "\r\n key1: %%T"
+ "\r\n key2: %d"
+ "\r\n", key1, key2) );
+
+ if (COMPARE(key1, atom_options) == 0) {
+
+ switch (key2) {
+ case ESOCK_OPT_LEVEL_SOCKET:
+ result = esock_supports_options_socket(env);
+ break;
+ case ESOCK_OPT_LEVEL_IP:
+ result = esock_supports_options_ip(env);
+ break;
+ case ESOCK_OPT_LEVEL_IPV6:
+ result = esock_supports_options_ipv6(env);
+ break;
+ case ESOCK_OPT_LEVEL_TCP:
+ result = esock_supports_options_tcp(env);
+ break;
+ case ESOCK_OPT_LEVEL_UDP:
+ result = esock_supports_options_udp(env);
+ break;
+ case ESOCK_OPT_LEVEL_SCTP:
+ result = esock_supports_options_sctp(env);
+ break;
+ default:
+ result = MKEL(env);
+ break;
+ }
+
+ } else {
+ result = MKEL(env);
+ }
+
+ return result;
}
-#endif
+#endif
#if !defined(__WIN32__)
@@ -5156,6 +5270,7 @@ ERL_NIF_TERM esock_supports_options_sctp(ErlNifEnv* env)
#endif
+#if 0
#if !defined(__WIN32__)
static
@@ -5228,6 +5343,7 @@ ERL_NIF_TERM esock_supports_netns(ErlNifEnv* env)
}
#endif
+#endif // #if 0
#if !defined(__WIN32__)
@@ -5432,7 +5548,9 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env,
"\r\n eopts: %T"
"\r\n", fd, eopts) );
+ MLOCK(data.cntMtx);
result = esock_open2(env, fd, eopts);
+ MUNLOCK(data.cntMtx);
}
break;
@@ -5476,8 +5594,9 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env,
return esock_make_error(env, esock_atom_einval);
}
+ MLOCK(data.cntMtx);
result = esock_open4(env, domain, type, proto, eopts);
-
+ MUNLOCK(data.cntMtx);
}
break;
@@ -5512,7 +5631,7 @@ ERL_NIF_TERM esock_open2(ErlNifEnv* env,
int fd,
ERL_NIF_TERM eopts)
{
- BOOLEAN_T dbg = esock_open2_is_debug(env, eopts);
+ BOOLEAN_T dbg = esock_open_is_debug(env, eopts, data.sockDbg);
ESockDescriptor* descP;
ERL_NIF_TERM res, reason;
int domain, type, protocol;
@@ -5521,7 +5640,7 @@ ERL_NIF_TERM esock_open2(ErlNifEnv* env,
SOCKET sock;
HANDLE event;
- SGDBG2( dbg,
+ SSDBG2( dbg,
("SOCKET", "esock_open2 -> entry with"
"\r\n fd: %d"
"\r\n eopts: %T"
@@ -5529,46 +5648,46 @@ ERL_NIF_TERM esock_open2(ErlNifEnv* env,
/*
* Before we do anything else, we try to retrieve domain, type and protocol
- * This information is either present in the eextra map or if not we need
+ * This information is either present in the eopts map or if not we need
* to "get" it from the system (getsockopt).
* Note that its not possible to get all of these on all platoforms,
- * and in those cases the user *must* provide us with them (eextra).
+ * and in those cases the user *must* provide us with them (eopts).
*
* We try the system first (since its more reliable) and if that fails
- * we check the eextra map. If neither one works, we *give up*!
+ * we check the eopts map. If neither one works, we *give up*!
*/
if (!esock_open_which_domain(fd, &domain)) {
- SGDBG2( dbg,
+ SSDBG2( dbg,
("SOCKET",
"esock_open2 -> failed get domain from system\r\n") );
if (!esock_open2_get_domain(env, eopts, &domain)) {
- reason = MKT2(env, atom_missing, esock_atom_domain);
+ reason = MKA(env, "epfnosupport");
return esock_make_error(env, reason);
}
}
if (!esock_open_which_type(fd, &type)) {
- SGDBG2( dbg,
+ SSDBG2( dbg,
("SOCKET", "esock_open2 -> failed get type from system\r\n") );
if (!esock_open2_get_type(env, eopts, &type)) {
- reason = MKT2(env, atom_missing, esock_atom_type);
+ reason = MKA(env, "esocktnosupport");
return esock_make_error(env, reason);
}
}
if (!esock_open_which_protocol(fd, &protocol)) {
- SGDBG2( dbg,
+ SSDBG2( dbg,
("SOCKET", "esock_open2 -> failed get protocol from system\r\n") );
if (!esock_open2_get_protocol(env, eopts, &protocol)) {
- reason = MKT2(env, atom_missing, esock_atom_protocol);
+ reason = MKA(env, "eprotonosupport");
return esock_make_error(env, reason);
}
}
- SGDBG2( dbg,
+ SSDBG2( dbg,
("SOCKET", "esock_open2 -> "
"\r\n domain: %d"
"\r\n type: %d"
@@ -5581,11 +5700,12 @@ ERL_NIF_TERM esock_open2(ErlNifEnv* env,
if (IS_SOCKET_ERROR(sock = dup(fd))) {
save_errno = sock_errno();
- SGDBG2( dbg,
- ("SOCKET", "esock_open2 -> dup failed: %d\r\n", save_errno) );
+ SSDBG2( dbg,
+ ("SOCKET",
+ "esock_open2 -> dup failed: %d\r\n",
+ save_errno) );
- /* reason = {dup, 'errno atom'} */
- reason = MKT2(env, MKA(env, "dup"), MKA(env, erl_errno_id(save_errno)));
+ reason = MKA(env, erl_errno_id(save_errno));
return esock_make_error(env, reason);
}
closeOnClose = TRUE;
@@ -5611,17 +5731,17 @@ ERL_NIF_TERM esock_open2(ErlNifEnv* env,
descP->closeOnClose = closeOnClose;
descP->origFD = fd;
- {
- /* Check if we are already connected, if so change state */
- unsigned int sz = sizeof(descP->remote);
- int code = sock_peer(descP->sock,
- (struct sockaddr*) &descP->remote, &sz);
- if (! IS_SOCKET_ERROR(code)) {
- SGDBG2( dbg, ("SOCKET", "esock_open2 -> connected\r\n") );
- descP->writeState = ESOCK_STATE_CONNECTED;
- } else {
- SGDBG2( dbg, ("SOCKET", "esock_open2 -> not connected\r\n") );
- }
+ /* Check if we are already connected, if so change state */
+ descP->addrLen = sizeof(descP->remote);
+ sys_memzero((char *) &descP->remote, descP->addrLen);
+ if (sock_peer(descP->sock,
+ (struct sockaddr*) &descP->remote,
+ &descP->addrLen) == 0) {
+ SSDBG2( dbg, ("SOCKET", "esock_open2 -> connected\r\n") );
+ descP->writeState |= ESOCK_STATE_CONNECTED;
+ } else {
+ SSDBG2( dbg, ("SOCKET", "esock_open2 -> not connected\r\n") );
+ descP->addrLen = 0;
}
/* And create the 'socket' resource */
@@ -5640,9 +5760,10 @@ ERL_NIF_TERM esock_open2(ErlNifEnv* env,
env, descP,
&descP->ctrlPid,
&descP->ctrlMon) != 0)
- return esock_make_error(env, atom_exmon);
+ return esock_make_error(env, atom_exmonitor);
+ descP->dbg = dbg;
inc_socket(domain, type, protocol);
/* And finally update the registry.
@@ -5650,29 +5771,19 @@ ERL_NIF_TERM esock_open2(ErlNifEnv* env,
*/
esock_send_reg_add_msg(env, res);
- SGDBG2( dbg,
+ SSDBG2( dbg,
("SOCKET", "esock_open2 -> done: %T\r\n", res) );
return esock_make_ok2(env, res);
}
-/* The eextra map "may" contain a boolean 'debug' key, if not we assume
- * the default of FALSE.
- */
-static
-BOOLEAN_T esock_open2_is_debug(ErlNifEnv* env, ERL_NIF_TERM eextra)
-{
- return esock_get_bool_from_map(env, eextra, MKA(env, "debug"), FALSE);
-}
-
-
/* The eextra contains a boolean 'dup' key. Defaults to TRUE.
*/
static
BOOLEAN_T esock_open2_todup(ErlNifEnv* env, ERL_NIF_TERM eextra)
{
- return esock_get_bool_from_map(env, eextra, MKA(env, "dup"), TRUE);
+ return esock_get_bool_from_map(env, eextra, atom_dup, TRUE);
}
/* The eextra contains an integer 'domain' key.
@@ -5769,7 +5880,7 @@ ERL_NIF_TERM esock_open4(ErlNifEnv* env,
int protocol,
ERL_NIF_TERM eopts)
{
- BOOLEAN_T dbg = esock_open2_is_debug(env, eopts);
+ BOOLEAN_T dbg = esock_open_is_debug(env, eopts, data.sockDbg);
ESockDescriptor* descP;
ERL_NIF_TERM res;
int proto = protocol, save_errno;
@@ -5780,7 +5891,7 @@ ERL_NIF_TERM esock_open4(ErlNifEnv* env,
int current_ns = 0;
#endif
- SGDBG2( dbg,
+ SSDBG2( dbg,
("SOCKET", "esock_open4 -> entry with"
"\r\n domain: %d"
"\r\n type: %d"
@@ -5811,7 +5922,7 @@ ERL_NIF_TERM esock_open4(ErlNifEnv* env,
return esock_make_error_errno(env, sock_errno());
}
- SGDBG2( dbg, ("SOCKET", "esock_open -> open success: %d\r\n", sock) );
+ SSDBG2( dbg, ("SOCKET", "esock_open -> open success: %d\r\n", sock) );
/* NOTE that if the protocol = 0 (default) and the domain is not
@@ -5857,7 +5968,7 @@ ERL_NIF_TERM esock_open4(ErlNifEnv* env,
return esock_make_error_errno(env, save_errno);
}
- SGDBG2( dbg, ("SOCKET", "esock_open4 -> event success: %d\r\n", event) );
+ SSDBG2( dbg, ("SOCKET", "esock_open4 -> event success: %d\r\n", event) );
SET_NONBLOCKING(sock);
@@ -5896,14 +6007,12 @@ ERL_NIF_TERM esock_open4(ErlNifEnv* env,
&descP->ctrlPid,
&descP->ctrlMon) != 0) {
sock_close(sock);
- return esock_make_error(env, atom_exmon);
+ return esock_make_error(env, atom_exmonitor);
}
- MLOCK(data.cntMtx);
descP->dbg = data.sockDbg;
inc_socket(domain, type, protocol);
- MUNLOCK(data.cntMtx);
/* And finally update the registry */
esock_send_reg_add_msg(env, res);
@@ -5912,6 +6021,16 @@ ERL_NIF_TERM esock_open4(ErlNifEnv* env,
}
+/* The eextra map "may" contain a boolean 'debug' key.
+ */
+static
+BOOLEAN_T esock_open_is_debug(ErlNifEnv* env, ERL_NIF_TERM eextra,
+ BOOLEAN_T dflt)
+{
+ return esock_get_bool_from_map(env, eextra, MKA(env, "debug"), dflt);
+}
+
+
static
BOOLEAN_T esock_open_which_domain(SOCKET sock, int* domain)
{
@@ -6213,37 +6332,54 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env,
return enif_raise_exception(env, MKA(env, "notsup"));
#else
ESockDescriptor* descP;
- ERL_NIF_TERM res, eSockAddr, sockRef;
+ ERL_NIF_TERM res, sockRef;
char* xres;
- ESockAddress addr;
+ ESockAddress addr, *addrP;
socklen_t addrLen;
SGDBG( ("SOCKET", "nif_connect -> entry with argc: %d\r\n", argc) );
/* Extract arguments and perform preliminary validation */
- sockRef = argv[0];
- if ((argc != 2) ||
- !ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP)) {
+ if (argc >= 1) {
+ sockRef = argv[0];
+ if (! ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP))
+ return enif_make_badarg(env);
+ } else {
return enif_make_badarg(env);
}
- eSockAddr = argv[1];
- if ((xres = esock_decode_sockaddr(env, eSockAddr, &addr, &addrLen))
- != NULL) {
- return esock_make_error_str(env, xres);
- }
+ if (argc >= 2) {
+ ERL_NIF_TERM eSockAddr = argv[1];
- MLOCK(descP->writeMtx);
+ if ((xres = esock_decode_sockaddr(env, eSockAddr, &addr, &addrLen))
+ != NULL) {
+ return esock_make_error_str(env, xres);
+ }
+ addrP = &addr;
- SSDBG( descP,
- ("SOCKET", "nif_connect(%T), {%d,%s,0x%X} ->"
- "\r\n SockAddr: %T"
- "\r\n",
- sockRef, descP->sock, B2S(descP->closing), descP->writeState,
- eSockAddr) );
+ MLOCK(descP->writeMtx);
+
+ SSDBG( descP,
+ ("SOCKET", "nif_connect(%T), {%d,%s,0x%X} ->"
+ "\r\n SockAddr: %T"
+ "\r\n",
+ sockRef, descP->sock, B2S(descP->closing), descP->writeState,
+ eSockAddr) );
+ } else {
+ addrP = NULL;
+ addrLen = 0;
+
+ MLOCK(descP->writeMtx);
+
+ SSDBG( descP,
+ ("SOCKET", "nif_connect(%T), {%d,%s,0x%X} ->"
+ "\r\n",
+ sockRef, descP->sock, B2S(descP->closing), descP->writeState
+ ) );
+ }
- res = esock_connect(env, descP, sockRef, &addr, addrLen);
+ res = esock_connect(env, descP, sockRef, addrP, addrLen);
SSDBG( descP, ("SOCKET", "nif_connect(%T) -> done with"
"\r\n res: %T"
@@ -6265,7 +6401,7 @@ ERL_NIF_TERM esock_connect(ErlNifEnv* env,
ESockAddress* addrP,
socklen_t addrLen)
{
- int code, sres, save_errno;
+ int sres, save_errno;
ErlNifPid self;
/*
@@ -6284,53 +6420,55 @@ ERL_NIF_TERM esock_connect(ErlNifEnv* env,
if (descP->currentWriterP != NULL)
return esock_make_error(env, esock_atom_einval);
- /* Either this should be the first connect attempt,
- * or if it is a subsequent it should be from the same process
- */
- if (descP->connectorP != NULL &&
- COMPARE_PIDS(&self, &descP->connector.pid) != 0) {
- return esock_make_error_errno(env, EALREADY);
- }
+ if (descP->connectorP != NULL) {
+ /* Connect in progress */
- /* connectorP is NULL or self - connect */
+ if (COMPARE_PIDS(&self, &descP->connector.pid) != 0) {
+ /* Other process has connect in progress */
+ return esock_make_error_errno(env, EALREADY);
+ }
- code = sock_connect(descP->sock, (struct sockaddr*) addrP, addrLen);
- if (IS_SOCKET_ERROR(code))
- save_errno = sock_errno();
- else
- save_errno = 0;
+ /* Finalize after received select message */
- SSDBG( descP,
- ("SOCKET", "esock_connect {%d} -> connect result: %d, %d\r\n",
- descP->sock, code, save_errno) );
+ requestor_release("esock_connect finalize -> connected",
+ env, descP, descP->connectorP);
+ descP->connectorP = NULL;
- switch (save_errno) {
- case 0: /* No error */
+ descP->writeState &= ~ESOCK_STATE_CONNECTING;
+
+ if (! verify_is_connected(descP, &save_errno)) {
+ return esock_make_error_errno(env, save_errno);
+ }
+
+ descP->writeState |= ESOCK_STATE_CONNECTED;
+
+ return esock_atom_ok;
+ }
+
+ /* No connect in progress */
+
+ if (addrP == NULL)
+ return esock_make_error(env, esock_atom_einval);
+
+ /* Initial connect call, with address */
+
+ if (sock_connect(descP->sock, (struct sockaddr*) addrP, addrLen) == 0) {
+ /* Success already! */
SSDBG( descP, ("SOCKET", "esock_connect {%d} -> connected\r\n",
descP->sock) );
- {
- int err;
- sys_memcpy(&descP->remote, addrP, addrLen);
- descP->addrLen = addrLen;
+ sys_memcpy(&descP->remote, addrP, addrLen);
+ descP->addrLen = addrLen;
- if (descP->connectorP != NULL) {
- requestor_release("esock_connect -> connected",
- env, descP, &descP->connector);
- descP->connectorP = NULL;
- }
-
- descP->writeState &= ~ESOCK_STATE_CONNECTING;
+ descP->writeState |= ESOCK_STATE_CONNECTED;
- if (! verify_is_connected(descP, &err)) {
- return esock_make_error_errno(env, err);
- }
+ return esock_atom_ok;
+ }
- descP->writeState |= ESOCK_STATE_CONNECTED;
+ /* Connect returned error */
+ save_errno = sock_errno();
- return esock_atom_ok;
- }
- break;
+ switch (save_errno) {
case ERRNO_BLOCK: /* Winsock2 */
case EINPROGRESS: /* Unix & OSE!! */
@@ -6338,23 +6476,17 @@ ERL_NIF_TERM esock_connect(ErlNifEnv* env,
("SOCKET", "esock_connect {%d} -> would block => select\r\n",
descP->sock) );
{
- if (descP->connectorP == NULL) {
+ /* Initiate connector */
- /* First time here - initiate connector */
+ descP->connector.pid = self;
+ if (MONP("esock_connect -> conn",
+ env, descP, &self, &descP->connector.mon) != 0) {
- descP->connector.pid = self;
- if (MONP("esock_connect -> conn",
- env, descP, &self, &descP->connector.mon) != 0) {
-
- MON_INIT(&descP->connector.mon);
- return esock_make_error(env, atom_exmon);
- }
- descP->connector.env = esock_alloc_env("connector");
- descP->connectorP = &descP->connector;
- } else
- enif_clear_env(descP->connector.env);
-
- /* A retry overwrites with a new ref */
+ MON_INIT(&descP->connector.mon);
+ return esock_make_error(env, atom_exmonitor);
+ }
+ descP->connector.env = esock_alloc_env("connector");
+ descP->connectorP = &descP->connector;
descP->connector.ref = MKREF(descP->connector.env);
if ((sres =
@@ -6366,157 +6498,28 @@ ERL_NIF_TERM esock_connect(ErlNifEnv* env,
requestor_release("esock_connect -> select failed",
env, descP, descP->connectorP);
descP->connectorP = NULL;
- return esock_make_error(env,
- MKT2(env,
- esock_atom_select_failed,
- MKI(env, sres)));
+ return esock_make_error(env, atom_exselect);
} else {
-
- sys_memcpy(&descP->remote, addrP, addrLen);
- descP->addrLen = addrLen;
-
descP->writeState |= ESOCK_STATE_CONNECTING;
-
return
- esock_make_ok2(env,
- CP_TERM(env, descP->connector.ref));
- }
- }
- break;
-
- case EISCONN:
- SSDBG( descP,
- ("SOCKET", "esock_connect {%d} -> *already* connected\r\n",
- descP->sock) );
- {
- int err;
-
- if (descP->connectorP != NULL) {
- requestor_release("esock_connect -> already connected",
- env, descP, descP->connectorP);
- descP->connectorP = NULL;
+ MKT2(env, atom_select,
+ CP_TERM(env, descP->connector.ref));
}
-
- descP->writeState &= ~ESOCK_STATE_CONNECTING;
-
- if (! verify_is_connected(descP, &err))
- return esock_make_error_errno(env, err);
-
- descP->writeState |= ESOCK_STATE_CONNECTED;
-
- return esock_atom_ok;
}
break;
default:
SSDBG( descP,
- ("SOCKET", "esock_connect {%d} -> other error: %d\r\n",
+ ("SOCKET", "esock_connect {%d} -> error: %d\r\n",
descP->sock, save_errno) );
- {
- if (descP->connectorP != NULL) {
- requestor_release("esock_connect -> already connected",
- env, descP, descP->connectorP);
- descP->connectorP = NULL;
- }
- return esock_make_error_errno(env, save_errno);
- }
- break;
- }
-}
-#endif // if !defined(__WIN32__)
-
-
-/* ----------------------------------------------------------------------
- * nif_finalize_connection
- *
- * Description:
- * Make socket ready for input and output.
- * This function is called if we where made to wait when we called the
- * nif_connect function (we made a select, and the select message has
- * now been received).
- *
- * Arguments:
- * Socket (ref) - Points to the socket descriptor.
- */
-static
-ERL_NIF_TERM nif_finalize_connection(ErlNifEnv* env,
- int argc,
- const ERL_NIF_TERM argv[])
-{
- ERL_NIF_TERM ret;
- ESockDescriptor* descP;
-#if defined(__WIN32__)
- return enif_raise_exception(env, MKA(env, "notsup"));
-#else
-
- /* Extract arguments and perform preliminary validation */
-
- if ((argc != 1) ||
- !ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP)) {
- return enif_make_badarg(env);
- }
-
- MLOCK(descP->writeMtx);
-
- SSDBG( descP,
- ("SOCKET", "nif_finalize_connection(%T), {%d,%s,0x%X}"
- "\r\n", argv[0],
- descP->sock, B2S(descP->closing), descP->writeState) );
-
- ret = esock_finalize_connection(env, descP);
-
- SSDBG( descP, ("SOCKET", "nif_finalize_connection(%T) -> done with"
- "\r\n ret: %T"
- "\r\n", argv[0], ret) );
-
- MUNLOCK(descP->writeMtx);
+ return esock_make_error_errno(env, save_errno);
- return ret;
-#endif
+ } // switch(save_errno)
}
+#endif // if !defined(__WIN32__)
-/* *** esock_finalize_connection ***
- * Perform the final check to verify a connection.
- */
-#if !defined(__WIN32__)
-static
-ERL_NIF_TERM esock_finalize_connection(ErlNifEnv* env,
- ESockDescriptor* descP)
-{
- int error;
- ErlNifPid self;
-
- /* Verify that we are in the proper state */
-
- if (! IS_OPEN(descP))
- return esock_make_error(env, atom_closed);
-
- if (descP->connectorP == NULL)
- return esock_make_error(env, esock_atom_einval);
-
- if (enif_self(env, &self) == NULL)
- return esock_make_error(env, atom_exself);
-
- if (COMPARE_PIDS(&self, &descP->connector.pid) != 0)
- return esock_make_error(env, esock_atom_einval);
-
- requestor_release("esock_finalize_connection -> connected",
- env, descP, descP->connectorP);
- descP->connectorP = NULL;
-
- descP->writeState &= ~ESOCK_STATE_CONNECTING;
-
- if (! verify_is_connected(descP, &error))
- return esock_make_error_errno(env, error);
-
- descP->writeState |= ESOCK_STATE_CONNECTED;
-
- return esock_atom_ok;
-}
-#endif
-
/* *** verify_is_connected ***
* Check if a connection has been established.
@@ -6535,37 +6538,39 @@ BOOLEAN_T verify_is_connected(ESockDescriptor* descP, int* err)
* discussion about Unix portability and non blocking connect.
*/
-#ifndef SO_ERROR
-
- int sz, code;
+ int code;
- sz = sizeof(descP->remote);
- sys_memzero((char *) &descP->remote, sz);
+ descP->addrLen = sizeof(descP->remote);
+ sys_memzero((char *) &descP->remote, descP->addrLen);
code = sock_peer(descP->sock,
- (struct sockaddr*) &descP->remote, &sz);
+ (struct sockaddr*) &descP->remote, &descP->addrLen);
if (IS_SOCKET_ERROR(code)) {
- *err = sock_errno();
- return FALSE;
- }
+ int save_errno = sock_errno();
-#else
+ descP->addrLen = 0;
+
+#ifdef SO_ERROR
+ {
+ int error;
+ SOCKLEN_T sz = sizeof(error);
+
+ if ((sock_getopt(descP->sock, SOL_SOCKET, SO_ERROR,
+ (void *)&error, &sz) == 0)
+ && (sz == sizeof(error))
+ && (error != 0)) {
- int error = 0; /* Has to be initiated, we check it */
- unsigned int sz = sizeof(error); /* even if we get -1 */
- int code = sock_getopt(descP->sock,
- SOL_SOCKET, SO_ERROR,
- (void *)&error, &sz);
+ *err = error;
+ return FALSE;
+ }
+ }
+#endif
- if ((code < 0) || error) {
- *err = error;
+ *err = save_errno;
return FALSE;
}
-#endif /* SO_ERROR */
-
*err = 0;
-
return TRUE;
}
#endif
@@ -6847,7 +6852,7 @@ ERL_NIF_TERM esock_accept_listening_error(ErlNifEnv* env,
&descP->currentAcceptor.pid,
&descP->currentAcceptor.mon) != 0) {
enif_set_pid_undefined(&descP->currentAcceptor.pid);
- res = esock_make_error(env, atom_exmon);
+ res = esock_make_error(env, atom_exmonitor);
} else {
ESOCK_ASSERT(!descP->currentAcceptor.env);
descP->currentAcceptor.env = esock_alloc_env("current acceptor");
@@ -7074,7 +7079,7 @@ ERL_NIF_TERM esock_accept_busy_retry(ErlNifEnv* env,
ErlNifPid* pid)
{
int sres;
- ERL_NIF_TERM res, reason;
+ ERL_NIF_TERM res;
if ((sres = esock_select_read(env, descP->sock, descP, pid,
sockRef, accRef)) < 0) {
@@ -7095,11 +7100,10 @@ ERL_NIF_TERM esock_accept_busy_retry(ErlNifEnv* env,
descP->currentAcceptorP = NULL;
}
- reason = MKT2(env, esock_atom_select_failed, MKI(env, sres));
- res = esock_make_error(env, reason);
+ res = esock_make_error(env, atom_exselect);
} else {
descP->readState |= ESOCK_STATE_ACCEPTING;
- res = esock_make_error(env, esock_atom_eagain); // OK!!
+ res = esock_make_error(env, esock_atom_eagain); // OK!!
}
return res;
@@ -7171,7 +7175,7 @@ BOOLEAN_T esock_accept_accepted(ErlNifEnv* env,
sock_close(accSock);
enif_set_pid_undefined(&descP->ctrlPid);
MUNLOCK(descP->writeMtx);
- *result = esock_make_error(env, atom_exmon);
+ *result = esock_make_error(env, atom_exmonitor);
return FALSE;
}
@@ -7179,7 +7183,6 @@ BOOLEAN_T esock_accept_accepted(ErlNifEnv* env,
accDescP->addrLen = len;
SET_NONBLOCKING(accDescP->sock);
- descP->readState &= ~ESOCK_STATE_ACCEPTING;
descP->writeState |= ESOCK_STATE_CONNECTED;
MUNLOCK(descP->writeMtx);
@@ -8420,7 +8423,7 @@ ERL_NIF_TERM esock_close(ErlNifEnv* env,
&descP->closerMon) != 0) {
enif_set_pid_undefined(&descP->closerPid);
- return esock_make_error(env, atom_exmon);
+ return esock_make_error(env, atom_exmonitor);
}
}
@@ -8434,8 +8437,6 @@ ERL_NIF_TERM esock_close(ErlNifEnv* env,
sres = esock_do_stop(env, descP);
if (sres < 0) { // Error
- ERL_NIF_TERM reason;
-
/* Calling esock_select_stop failed in some mysterious way,
* we are kind of toasted - we'll leave the socket leaked
* with descP->closing == TRUE
@@ -8467,8 +8468,7 @@ ERL_NIF_TERM esock_close(ErlNifEnv* env,
esock_free_env("esock_close_do - close-env", descP->closeEnv);
descP->closeEnv = NULL;
descP->closeRef = esock_atom_undefined;
- reason = MKT2(env, esock_atom_select_failed, MKI(env, sres));
- return esock_make_error(env, reason);
+ return esock_make_error(env, atom_exselect);
}
@@ -8704,18 +8704,21 @@ ERL_NIF_TERM esock_finalize_close(ErlNifEnv* env,
int err;
ErlNifPid self;
- if (IS_CLOSED(descP) || (! IS_CLOSING(descP)))
- return esock_make_error(env, atom_enotclosing);
+ if (IS_CLOSED(descP))
+ return esock_make_error(env, atom_closed);
+
+ if (! IS_CLOSING(descP))
+ return esock_make_error_errno(env, EALREADY);
if (enif_self(env, &self) == NULL)
return esock_make_error(env, atom_exself);
if (COMPARE_PIDS(&descP->closerPid, &self) != 0)
- return esock_make_error(env, atom_enotclosing);
+ return esock_make_error_errno(env, EALREADY);
/* closeEnv should be NULL or else esock_stop() has not been called */
if (descP->closeEnv != NULL)
- return esock_make_error(env, atom_enotclosing);
+ return esock_make_error_errno(env, EALREADY);
/* This process is the closer - go ahead and close the socket */
@@ -8740,7 +8743,7 @@ ERL_NIF_TERM esock_finalize_close(ErlNifEnv* env,
err = esock_close_socket(env, descP);
if (err != 0) {
- if (err != ERRNO_BLOCK) {
+ if (err == ERRNO_BLOCK) {
/* Not all data in the buffers where sent,
* make sure the caller gets this.
*/
@@ -8807,9 +8810,6 @@ ERL_NIF_TERM nif_shutdown(ErlNifEnv* env,
return enif_make_badarg(env);
}
- if (! IS_OPEN(descP))
- return esock_make_error(env, atom_closed);
-
if (!ehow2how(ehow, &how))
return enif_make_badarg(env);
@@ -8845,6 +8845,9 @@ ERL_NIF_TERM esock_shutdown(ErlNifEnv* env,
ESockDescriptor* descP,
int how)
{
+ if (! IS_OPEN(descP))
+ return esock_make_error(env, atom_closed);
+
if (sock_shutdown(descP->sock, how) == 0)
return esock_atom_ok;
else
@@ -8882,10 +8885,8 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env,
return enif_raise_exception(env, MKA(env, "notsup"));
#else
ESockDescriptor* descP = NULL;
- int eLevel, level = -1;
- int eOpt;
- ERL_NIF_TERM eIsEncoded;
- ERL_NIF_TERM eVal;
+ int eLevel, level = -1, eOpt;
+ ERL_NIF_TERM eIsEncoded, eVal;
BOOLEAN_T isEncoded, isOTP;
SGDBG( ("SOCKET", "nif_setopt -> entry with argc: %d\r\n", argc) );
@@ -8913,11 +8914,10 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env,
return esock_make_error(env, esock_atom_einval);
}
- if (! IS_OPEN(descP))
- return esock_make_error(env, atom_closed);
-
+ MLOCK(descP->readMtx);
return esock_setopt(env, descP, isEncoded, isOTP, level, eOpt, eVal);
-
+ /* Surprise! MUNLOCK in called function */
+
#endif // if defined(__WIN32__)
}
@@ -8932,20 +8932,21 @@ ERL_NIF_TERM esock_setopt(ErlNifEnv* env,
int eOpt,
ERL_NIF_TERM eVal)
{
- ERL_NIF_TERM result;
+ if (! IS_OPEN(descP)) {
+ MUNLOCK(descP->readMtx);
+ return esock_make_error(env, atom_closed);
+ }
if (isOTP) {
/* These are not actual socket options,
* but options for our implementation.
*/
- result = esock_setopt_otp(env, descP, eOpt, eVal);
- } else if (!isEncoded) {
- result = esock_setopt_native(env, descP, level, eOpt, eVal);
+ return esock_setopt_otp(env, descP, eOpt, eVal);
+ } else if (! isEncoded) {
+ return esock_setopt_native(env, descP, level, eOpt, eVal);
} else {
- result = esock_setopt_level(env, descP, level, eOpt, eVal);
+ return esock_setopt_level(env, descP, level, eOpt, eVal);
}
-
- return result;
}
@@ -8962,7 +8963,6 @@ ERL_NIF_TERM esock_setopt_otp(ErlNifEnv* env,
switch (eOpt) {
case ESOCK_OPT_OTP_DEBUG:
- MLOCK(descP->readMtx);
MLOCK(descP->writeMtx);
result = esock_setopt_otp_debug(env, descP, eVal);
MUNLOCK(descP->writeMtx);
@@ -8970,7 +8970,6 @@ ERL_NIF_TERM esock_setopt_otp(ErlNifEnv* env,
break;
case ESOCK_OPT_OTP_IOW:
- MLOCK(descP->readMtx);
MLOCK(descP->writeMtx);
result = esock_setopt_otp_iow(env, descP, eVal);
MUNLOCK(descP->writeMtx);
@@ -8978,7 +8977,6 @@ ERL_NIF_TERM esock_setopt_otp(ErlNifEnv* env,
break;
case ESOCK_OPT_OTP_CTRL_PROC:
- MLOCK(descP->readMtx);
MLOCK(descP->writeMtx);
result = esock_setopt_otp_ctrl_proc(env, descP, eVal);
MUNLOCK(descP->writeMtx);
@@ -8986,31 +8984,32 @@ ERL_NIF_TERM esock_setopt_otp(ErlNifEnv* env,
break;
case ESOCK_OPT_OTP_RCVBUF:
- MLOCK(descP->readMtx);
result = esock_setopt_otp_rcvbuf(env, descP, eVal);
MUNLOCK(descP->readMtx);
break;
case ESOCK_OPT_OTP_RCVCTRLBUF:
- MLOCK(descP->readMtx);
result = esock_setopt_otp_rcvctrlbuf(env, descP, eVal);
MUNLOCK(descP->readMtx);
break;
case ESOCK_OPT_OTP_SNDCTRLBUF:
MLOCK(descP->writeMtx);
+ MUNLOCK(descP->readMtx);
result = esock_setopt_otp_sndctrlbuf(env, descP, eVal);
MUNLOCK(descP->writeMtx);
break;
case ESOCK_OPT_OTP_META:
MLOCK(descP->writeMtx);
+ MUNLOCK(descP->readMtx);
result = esock_setopt_otp_meta(env, descP, eVal);
MUNLOCK(descP->writeMtx);
break;
default:
MLOCK(descP->writeMtx);
+ MUNLOCK(descP->readMtx);
SSDBG( descP,
("SOCKET", "esock_setopt_otp {%d} -> einval with"
"\r\n eOpt: %d"
@@ -9304,6 +9303,7 @@ ERL_NIF_TERM esock_setopt_native(ErlNifEnv* env,
ERL_NIF_TERM result;
MLOCK(descP->writeMtx);
+ MUNLOCK(descP->readMtx);
SSDBG( descP,
("SOCKET", "esock_setopt_native {%d} -> entry"
@@ -9348,6 +9348,7 @@ ERL_NIF_TERM esock_setopt_level(ErlNifEnv* env,
ERL_NIF_TERM result;
MLOCK(descP->writeMtx);
+ MUNLOCK(descP->readMtx);
SSDBG( descP,
("SOCKET", "esock_setopt_level {%d} -> entry with"
@@ -12668,10 +12669,9 @@ ERL_NIF_TERM nif_getopt(ErlNifEnv* env,
if (!elevel2level(isEncoded, eLevel, &isOTP, &level))
return esock_make_error(env, esock_atom_einval);
- if (! IS_OPEN(descP))
- return esock_make_error(env, atom_closed);
-
+ MLOCK(descP->readMtx);
return esock_getopt(env, descP, isEncoded, isOTP, level, eOpt);
+ /* Surprise! MUNLOCK in called function */
#endif // if defined(__WIN32__)
}
@@ -12698,19 +12698,21 @@ ERL_NIF_TERM esock_getopt(ErlNifEnv* env,
"\r\n eOpt: %T"
"\r\n", B2S(isEncoded), B2S(isOTP), level, eOpt) );
- if (isOTP) {
+ if (! IS_OPEN(descP)) {
+ result = esock_make_error(env, atom_closed);
+ } else if (isOTP) {
/* These are not actual socket options,
* but options for our implementation.
*/
if (GET_INT(env, eOpt, &opt))
- result = esock_getopt_otp(env, descP, opt);
+ return esock_getopt_otp(env, descP, opt);
else
result = esock_make_error(env, esock_atom_einval);
} else if (!isEncoded) {
- result = esock_getopt_native(env, descP, level, eOpt);
+ return esock_getopt_native(env, descP, level, eOpt);
} else {
if (GET_INT(env, eOpt, &opt))
- result = esock_getopt_level(env, descP, level, opt);
+ return esock_getopt_level(env, descP, level, opt);
else
result = esock_make_error(env, esock_atom_einval);
}
@@ -12719,7 +12721,7 @@ ERL_NIF_TERM esock_getopt(ErlNifEnv* env,
("SOCKET", "esock_getopt -> done when"
"\r\n result: %T"
"\r\n", result) );
-
+ MUNLOCK(descP->readMtx);
return result;
}
@@ -12741,49 +12743,45 @@ ERL_NIF_TERM esock_getopt_otp(ErlNifEnv* env,
switch (eOpt) {
case ESOCK_OPT_OTP_DEBUG:
- MLOCK(descP->writeMtx);
result = esock_getopt_otp_debug(env, descP);
- MUNLOCK(descP->writeMtx);
+ MUNLOCK(descP->readMtx);
break;
case ESOCK_OPT_OTP_IOW:
- MLOCK(descP->writeMtx);
result = esock_getopt_otp_iow(env, descP);
- MUNLOCK(descP->writeMtx);
+ MUNLOCK(descP->readMtx);
break;
case ESOCK_OPT_OTP_CTRL_PROC:
- MLOCK(descP->writeMtx);
result = esock_getopt_otp_ctrl_proc(env, descP);
- MUNLOCK(descP->writeMtx);
+ MUNLOCK(descP->readMtx);
break;
case ESOCK_OPT_OTP_RCVBUF:
- MLOCK(descP->readMtx);
result = esock_getopt_otp_rcvbuf(env, descP);
MUNLOCK(descP->readMtx);
break;
case ESOCK_OPT_OTP_RCVCTRLBUF:
- MLOCK(descP->readMtx);
result = esock_getopt_otp_rcvctrlbuf(env, descP);
MUNLOCK(descP->readMtx);
break;
case ESOCK_OPT_OTP_SNDCTRLBUF:
MLOCK(descP->writeMtx);
+ MUNLOCK(descP->readMtx);
result = esock_getopt_otp_sndctrlbuf(env, descP);
MUNLOCK(descP->writeMtx);
break;
case ESOCK_OPT_OTP_FD:
- MLOCK(descP->readMtx);
result = esock_getopt_otp_fd(env, descP);
MUNLOCK(descP->readMtx);
break;
case ESOCK_OPT_OTP_META:
MLOCK(descP->writeMtx);
+ MUNLOCK(descP->readMtx);
result = esock_getopt_otp_meta(env, descP);
MUNLOCK(descP->writeMtx);
break;
@@ -12791,22 +12789,25 @@ ERL_NIF_TERM esock_getopt_otp(ErlNifEnv* env,
/* *** INTERNAL *** */
case ESOCK_OPT_OTP_DOMAIN:
result = esock_getopt_otp_domain(env, descP);
+ MUNLOCK(descP->readMtx);
break;
case ESOCK_OPT_OTP_TYPE:
result = esock_getopt_otp_type(env, descP);
+ MUNLOCK(descP->readMtx);
break;
case ESOCK_OPT_OTP_PROTOCOL:
result = esock_getopt_otp_protocol(env, descP);
+ MUNLOCK(descP->readMtx);
break;
case ESOCK_OPT_OTP_DTP:
result = esock_getopt_otp_dtp(env, descP);
+ MUNLOCK(descP->readMtx);
break;
default:
- MLOCK(descP->readMtx);
SSDBG( descP,
("SOCKET", "esock_getopt_otp {%d} -> einval with"
"\r\n eOpt: %d"
@@ -13165,8 +13166,6 @@ ERL_NIF_TERM esock_getopt_native(ErlNifEnv* env,
Uint16 valueType;
SOCKOPTLEN_T valueSz;
- MLOCK(descP->readMtx);
-
SSDBG( descP,
("SOCKET", "esock_getopt_native {%d} -> entry"
"\r\n level: %d"
@@ -13282,8 +13281,6 @@ ERL_NIF_TERM esock_getopt_level(ErlNifEnv* env,
{
ERL_NIF_TERM result;
- MLOCK(descP->readMtx);
-
SSDBG( descP,
("SOCKET", "esock_getopt_level {%d} -> entry with"
"\r\n level: %d"
@@ -16245,7 +16242,7 @@ BOOLEAN_T send_check_writer(ErlNifEnv* env,
if (!writer_search4pid(env, descP, &caller))
*checkResult = writer_push(env, descP, caller, ref);
else
- *checkResult = esock_make_error(env, atom_exbusy);
+ *checkResult = esock_make_error_errno(env, EALREADY);
SSDBG( descP,
("SOCKET",
@@ -16499,7 +16496,7 @@ ERL_NIF_TERM send_check_retry(ErlNifEnv* env,
&descP->currentWriter.pid,
&descP->currentWriter.mon) != 0) {
enif_set_pid_undefined(&descP->currentWriter.pid);
- return esock_make_error(env, atom_exmon);
+ return esock_make_error(env, atom_exmonitor);
} else {
ESOCK_ASSERT(descP->currentWriter.env == NULL);
descP->currentWriter.env = esock_alloc_env("current-writer");
@@ -16596,7 +16593,7 @@ BOOLEAN_T recv_check_reader(ErlNifEnv* env,
if (!reader_search4pid(env, descP, &caller))
*checkResult = reader_push(env, descP, caller, ref);
else
- *checkResult = esock_make_error(env, atom_exbusy);
+ *checkResult = esock_make_error_errno(env, EALREADY);
SSDBG( descP,
("SOCKET",
@@ -16637,7 +16634,7 @@ char* recv_init_current_reader(ErlNifEnv* env,
&descP->currentReader.pid,
&descP->currentReader.mon) != 0) {
enif_set_pid_undefined(&descP->currentReader.pid);
- return str_exmon;
+ return str_exmonitor;
} else {
ESOCK_ASSERT(!descP->currentReader.env);
descP->currentReader.env = esock_alloc_env("current-reader");
@@ -17144,7 +17141,7 @@ ERL_NIF_TERM recv_check_retry(ErlNifEnv* env,
/* Ouch
* Now what? We have copied ref into *its own* environment!
*/
- reason = MKT2(env, esock_atom_select_failed, MKI(env, sres));
+ reason = atom_exselect;
} else {
reason = esock_atom_eagain;
}
@@ -20132,93 +20129,73 @@ BOOLEAN_T esock_open4_get_netns(ErlNifEnv* env, ERL_NIF_TERM opts, char** netns)
static
BOOLEAN_T esendflags2sendflags(unsigned int eflags, int* flags)
{
- unsigned int ef;
- int tmp = 0;
+ int tmp = 0;
- /* First, check if we have any flags at all */
+ /* Optimize for no flags */
if (eflags == 0) {
*flags = 0;
return TRUE;
}
-
- for (ef = ESOCK_SEND_FLAG_LOW; ef <= ESOCK_SEND_FLAG_HIGH; ef++) {
- switch (ef) {
- case ESOCK_SEND_FLAG_CONFIRM:
- if ((1 << ESOCK_SEND_FLAG_CONFIRM) & eflags) {
+ /* Check for flags out of range */
+ if ((eflags & ~ESOCK_SEND_FLAG_MASK) != 0) {
+ esock_warning_msg("Use of unknown send flag (0x%lX)\r\n",
+ eflags);
+ }
+
+ if ((eflags & ESOCK_SEND_FLAG_CONFIRM) != 0) {
#if defined(MSG_CONFIRM)
- tmp |= MSG_CONFIRM;
+ tmp |= MSG_CONFIRM;
#else
- return FALSE;
+ return FALSE;
#endif
- }
- break;
+ }
- case ESOCK_SEND_FLAG_DONTROUTE:
- if ((1 << ESOCK_SEND_FLAG_DONTROUTE) & eflags) {
+ if ((eflags & ESOCK_SEND_FLAG_DONTROUTE) != 0) {
#if defined(MSG_DONTROUTE)
- tmp |= MSG_DONTROUTE;
+ tmp |= MSG_DONTROUTE;
#else
- return FALSE;
+ return FALSE;
#endif
- }
- break;
+ }
- case ESOCK_SEND_FLAG_EOR:
- if ((1 << ESOCK_SEND_FLAG_EOR) & eflags) {
+ if ((eflags & ESOCK_SEND_FLAG_EOR) != 0) {
#if defined(MSG_EOR)
- tmp |= MSG_EOR;
+ tmp |= MSG_EOR;
#else
- return FALSE;
+ return FALSE;
#endif
- }
- break;
+ }
- case ESOCK_SEND_FLAG_MORE:
- if ((1 << ESOCK_SEND_FLAG_MORE) & eflags) {
+ if ((eflags & ESOCK_SEND_FLAG_MORE) != 0) {
#if defined(MSG_MORE)
- tmp |= MSG_MORE;
+ tmp |= MSG_MORE;
#else
- return FALSE;
+ return FALSE;
#endif
- }
- break;
+ }
- case ESOCK_SEND_FLAG_NOSIGNAL:
- if ((1 << ESOCK_SEND_FLAG_NOSIGNAL) & eflags) {
+ if ((eflags & ESOCK_SEND_FLAG_NOSIGNAL) != 0) {
#if defined(MSG_NOSIGNAL)
- tmp |= MSG_NOSIGNAL;
+ tmp |= MSG_NOSIGNAL;
#else
- return FALSE;
+ return FALSE;
#endif
- }
- break;
+ }
- case ESOCK_SEND_FLAG_OOB:
- if ((1 << ESOCK_SEND_FLAG_OOB) & eflags) {
+ if ((eflags & ESOCK_SEND_FLAG_OOB) != 0) {
#if defined(MSG_OOB)
- tmp |= MSG_OOB;
+ tmp |= MSG_OOB;
#else
- return FALSE;
+ return FALSE;
#endif
- }
- break;
-
- default:
- esock_warning_msg("Use of unknown send flag %d (0x%lX)\r\n",
- ef, eflags);
- return FALSE;
- }
-
}
*flags = tmp;
-
return TRUE;
}
-
/* erecvflags2recvflags - convert internal (erlang) send flags to (proper)
* send flags.
*
@@ -20228,92 +20205,75 @@ BOOLEAN_T esendflags2sendflags(unsigned int eflags, int* flags)
static
BOOLEAN_T erecvflags2recvflags(unsigned int eflags, int* flags)
{
- unsigned int ef;
- int tmp = 0;
+ int tmp = 0;
+
+ /* Optimize for no flags */
if (eflags == 0) {
*flags = 0;
return TRUE;
}
- for (ef = ESOCK_RECV_FLAG_LOW; ef <= ESOCK_RECV_FLAG_HIGH; ef++) {
+ if ((eflags & ~ESOCK_RECV_FLAG_MASK) != 0) {
+ esock_warning_msg("Use of unknown recv flag (0x%lX)\r\n",
+ eflags);
+ }
- switch (ef) {
- case ESOCK_RECV_FLAG_CMSG_CLOEXEC:
- if ((1 << ESOCK_RECV_FLAG_CMSG_CLOEXEC) & eflags) {
+ if ((eflags & ESOCK_RECV_FLAG_CMSG_CLOEXEC) != 0) {
#if defined(MSG_CMSG_CLOEXEC)
- tmp |= MSG_CMSG_CLOEXEC;
+ tmp |= MSG_CMSG_CLOEXEC;
#else
- return FALSE;
+ return FALSE;
#endif
- }
- break;
+ }
- case ESOCK_RECV_FLAG_ERRQUEUE:
- if ((1 << ESOCK_RECV_FLAG_ERRQUEUE) & eflags) {
+ if ((eflags & ESOCK_RECV_FLAG_ERRQUEUE) != 0) {
#if defined(MSG_ERRQUEUE)
- tmp |= MSG_ERRQUEUE;
+ tmp |= MSG_ERRQUEUE;
#else
- return FALSE;
+ return FALSE;
#endif
- }
- break;
+ }
- case ESOCK_RECV_FLAG_OOB:
- if ((1 << ESOCK_RECV_FLAG_OOB) & eflags) {
+ if ((eflags & ESOCK_RECV_FLAG_OOB) != 0) {
#if defined(MSG_OOB)
- tmp |= MSG_OOB;
+ tmp |= MSG_OOB;
#else
- return FALSE;
+ return FALSE;
#endif
- }
- break;
+ }
- /*
- * <KOLLA>
- *
- * We need to handle this, because it may effect the read algorithm
- *
- * </KOLLA>
- */
- case ESOCK_RECV_FLAG_PEEK:
- if ((1 << ESOCK_RECV_FLAG_PEEK) & eflags) {
+ /*
+ * <KOLLA>
+ *
+ * We need to handle this, because it may effect the read algorithm
+ *
+ * </KOLLA>
+ */
+ if ((eflags & ESOCK_RECV_FLAG_PEEK) != 0) {
#if defined(MSG_PEEK)
- tmp |= MSG_PEEK;
+ tmp |= MSG_PEEK;
#else
- return FALSE;
+ return FALSE;
#endif
- }
- break;
+ }
- case ESOCK_RECV_FLAG_TRUNC:
- if ((1 << ESOCK_RECV_FLAG_TRUNC) & eflags) {
+ if ((eflags & ESOCK_RECV_FLAG_TRUNC) != 0) {
#if defined(MSG_TRUNC)
- tmp |= MSG_TRUNC;
+ tmp |= MSG_TRUNC;
#else
- return FALSE;
+ return FALSE;
#endif
- }
- break;
-
- default:
- esock_warning_msg("Use of unknown recv flag %d (0x%lX)\r\n",
- ef, eflags);
- return FALSE;
- }
-
}
*flags = tmp;
-
return TRUE;
}
-/* eproto2proto - convert internal (erlang) protocol to (proper) protocol
- *
- * Note that only a subset is supported.
+/* ehow2how - convert internal (erlang) "shutdown how" to
+ * (proper) "shutdown how"
*/
static
BOOLEAN_T ehow2how(unsigned int ehow, int* how)
@@ -20669,7 +20629,7 @@ ERL_NIF_TERM mk_select_msg(ErlNifEnv* env,
*
* {'$socket', Socket, Tag, Info}
*
- * Socket :: socket() (#socket{})
+ * Socket :: socket:socket()
* Tag :: atom()
* Info :: term()
*
@@ -20688,15 +20648,15 @@ ERL_NIF_TERM mk_socket_msg(ErlNifEnv* env,
/* *** mk_socket ***
*
- * Simple utility function that construct the socket resord:
+ * Simple utility function that construct the socket tuple:
*
- * #socket{ref = SockRef} => {socket, SockRef :: reference()}
+ * socket:socket() :: {'$socket', SockRef :: reference()}
*/
static
ERL_NIF_TERM mk_socket(ErlNifEnv* env,
ERL_NIF_TERM sockRef)
{
- return MKT2(env, esock_atom_socket, sockRef);
+ return MKT2(env, esock_atom_socket_tag, sockRef);
}
#endif // #if defined(__WIN32__)
@@ -20955,7 +20915,7 @@ REQ_SEARCH4PID_FUNCS
if (MONP("reader_push -> " #F " request", \
env, descP, &pid, &reqP->mon) != 0) { \
FREE(e); \
- return esock_make_error(env, atom_exmon); \
+ return esock_make_error(env, atom_exmonitor); \
} \
reqP->env = esock_alloc_env(#F "_push"); \
reqP->ref = CP_TERM(reqP->env, ref); \
@@ -21952,13 +21912,16 @@ ErlNifFunc esock_funcs[] =
// Some utility and support functions
{"nif_info", 0, nif_info, 0},
{"nif_info", 1, nif_info, 0},
+ {"nif_supports", 0, nif_supports, 0},
{"nif_supports", 1, nif_supports, 0},
+ {"nif_supports", 2, nif_supports, 0},
{"nif_command", 1, nif_command, 0},
// The proper "socket" interface
{"nif_open", 2, nif_open, 0},
{"nif_open", 4, nif_open, 0},
{"nif_bind", 2, nif_bind, 0},
+ {"nif_connect", 1, nif_connect, 0},
{"nif_connect", 2, nif_connect, 0},
{"nif_listen", 2, nif_listen, 0},
{"nif_accept", 2, nif_accept, 0},
@@ -21978,10 +21941,9 @@ ErlNifFunc esock_funcs[] =
/* Misc utility functions */
/* "Extra" functions to "complete" the socket interface.
- * For instance, the function nif_finalize_connection
- * is called after the connect *select* has "completed".
+ * For instance, the function nif_finalize_close
+ * is called after the close *select* has "completed".
*/
- {"nif_finalize_connection", 1, nif_finalize_connection, 0},
{"nif_cancel", 3, nif_cancel, 0},
{"nif_finalize_close", 1, nif_finalize_close, ERL_NIF_DIRTY_JOB_IO_BOUND}
};
@@ -22105,4 +22067,4 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
* NULL: THIS IS NOT USED
* unload: NULL (not used)
*/
-ERL_NIF_INIT(socket, esock_funcs, on_load, NULL, NULL, NULL)
+ERL_NIF_INIT(prim_socket, esock_funcs, on_load, NULL, NULL, NULL)
diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h
index 0095ddbe08..2f7428eb8e 100644
--- a/erts/emulator/nifs/common/socket_int.h
+++ b/erts/emulator/nifs/common/socket_int.h
@@ -113,7 +113,7 @@ typedef unsigned int BOOLEAN_T;
* "Global" atoms (esock_atom_...)
*
* Note that when an (global) atom is added here, it must also be added
- * in the socket_nif.c file!
+ * in the prim_socket_nif.c file!
*/
#define GLOBAL_ATOM_DEFS \
diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile
index e0e38e34f9..3825796322 100644
--- a/erts/emulator/test/Makefile
+++ b/erts/emulator/test/Makefile
@@ -28,21 +28,6 @@ EBIN = .
# Target Specs
# ----------------------------------------------------
-SOCKET_MODULES = \
- socket_test_lib \
- socket_test_logger \
- socket_test_evaluator \
- socket_test_ttest_lib \
- socket_test_ttest_tcp_gen \
- socket_test_ttest_tcp_socket \
- socket_test_ttest_tcp_client \
- socket_test_ttest_tcp_client_gen \
- socket_test_ttest_tcp_client_socket \
- socket_test_ttest_tcp_server \
- socket_test_ttest_tcp_server_gen \
- socket_test_ttest_tcp_server_socket \
- socket_SUITE
-
MODULES= \
a_SUITE \
after_SUITE \
@@ -123,7 +108,6 @@ MODULES= \
signal_SUITE \
small_SUITE \
smoke_test_SUITE \
- $(SOCKET_MODULES) \
statistics_SUITE \
system_info_SUITE \
system_profile_SUITE \
@@ -173,13 +157,8 @@ NATIVE_MODULES= $(NATIVE:%=%_native_SUITE)
NATIVE_ERL_FILES= $(NATIVE_MODULES:%=%.erl)
ERL_FILES= $(MODULES:%=%.erl)
-HRL_FILES= \
- socket_test_evaluator.hrl \
- socket_test_ttest.hrl \
- socket_test_ttest_client.hrl
TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
-SOCKET_TARGETS = $(SOCKET_MODULES:%=$(EBIN)/%.$(EMULATOR))
EMAKEFILE=Emakefile
@@ -223,7 +202,6 @@ clean:
docs:
targets: $(TARGET_FILES)
-socket_targets: $(SOCKET_TARGETS)
# ----------------------------------------------------
# Special targets
@@ -245,7 +223,7 @@ release_spec:
release_tests_spec: make_emakefile
$(INSTALL_DIR) "$(RELSYSDIR)"
$(INSTALL_DATA) $(EMAKEFILE) $(TEST_SPEC_FILES) \
- $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)"
+ $(ERL_FILES) "$(RELSYSDIR)"
$(INSTALL_DATA) $(NO_OPT_ERL_FILES) "$(RELSYSDIR)"
$(INSTALL_DATA) $(NATIVE_ERL_FILES) "$(RELSYSDIR)"
chmod -R u+w "$(RELSYSDIR)"
diff --git a/erts/preloaded/ebin/erl_init.beam b/erts/preloaded/ebin/erl_init.beam
index bcfc4d2e22..4f22f77a27 100644
--- a/erts/preloaded/ebin/erl_init.beam
+++ b/erts/preloaded/ebin/erl_init.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_net.beam b/erts/preloaded/ebin/prim_net.beam
index 6e8073ae5a..510856e5c0 100644
--- a/erts/preloaded/ebin/prim_net.beam
+++ b/erts/preloaded/ebin/prim_net.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_socket.beam b/erts/preloaded/ebin/prim_socket.beam
new file mode 100644
index 0000000000..61fa0afc55
--- /dev/null
+++ b/erts/preloaded/ebin/prim_socket.beam
Binary files differ
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
deleted file mode 100644
index e303eaa71d..0000000000
--- a/erts/preloaded/ebin/socket.beam
+++ /dev/null
Binary files differ
diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile
index d2153f23e1..b0c205cec8 100644
--- a/erts/preloaded/src/Makefile
+++ b/erts/preloaded/src/Makefile
@@ -36,7 +36,7 @@ include $(ERL_TOP)/lib/kernel/vsn.mk
ifeq ($(USE_ESOCK), yes)
PRE_LOADED_ERL_ESOCK_MODULES = \
socket_registry \
- socket \
+ prim_socket \
prim_net
else
PRE_LOADED_ERL_ESOCK_MODULES =
@@ -82,7 +82,7 @@ APP_FILE= erts.app
APP_SRC= $(APP_FILE).src
APP_TARGET= $(STATIC_EBIN)/$(APP_FILE)
ifeq ($(USE_ESOCK), yes)
-APP_ESOCK_MODS= prim_net, socket, socket_registry
+APP_ESOCK_MODS= prim_net, prim_socket, socket_registry,
else
APP_ESOCK_MODS=
endif
diff --git a/erts/preloaded/src/erl_init.erl b/erts/preloaded/src/erl_init.erl
index dadf7dda6f..c19bebdd20 100644
--- a/erts/preloaded/src/erl_init.erl
+++ b/erts/preloaded/src/erl_init.erl
@@ -35,8 +35,8 @@ start(Mod, BootArgs) ->
erl_tracer:on_load(),
prim_buffer:on_load(),
prim_file:on_load(),
- %% socket:on_load(), prim_net:on_load(),
- conditional_load(socket, [socket, prim_net]),
+ %% prim_socket:on_load(), prim_net:on_load(),
+ conditional_load(prim_socket, [prim_socket, prim_net]),
%% Proceed to the specified boot module
run(Mod, boot, BootArgs).
diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src
index f8b2338c44..c3e5182a60 100644
--- a/erts/preloaded/src/erts.app.src
+++ b/erts/preloaded/src/erts.app.src
@@ -36,8 +36,8 @@
atomics,
counters,
persistent_term,
- zlib,
%ESOCK_MODS%
+ zlib
]},
{registered, []},
{applications, []},
diff --git a/erts/preloaded/src/prim_net.erl b/erts/preloaded/src/prim_net.erl
index fcd528bdaa..e82ae33b8a 100644
--- a/erts/preloaded/src/prim_net.erl
+++ b/erts/preloaded/src/prim_net.erl
@@ -157,15 +157,14 @@ gethostname() ->
Info :: name_info(),
Reason :: term().
-getnameinfo(SockAddr, [] = _Flags) ->
- getnameinfo(SockAddr, undefined);
-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).
+getnameinfo(SockAddr, Flags) ->
+ try
+ ESockAddr = prim_socket:encode_sockaddr(SockAddr),
+ nif_getnameinfo(ESockAddr, Flags)
+ catch
+ throw : ERROR ->
+ ERROR
+ end.
%% ===========================================================================
diff --git a/erts/preloaded/src/prim_socket.erl b/erts/preloaded/src/prim_socket.erl
new file mode 100644
index 0000000000..0523f8db60
--- /dev/null
+++ b/erts/preloaded/src/prim_socket.erl
@@ -0,0 +1,1302 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2020. 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(prim_socket).
+
+-compile(no_native).
+
+-export([on_load/0, on_load/1]).
+
+-export(
+ [encode_path/1, encode_sockaddr/1,
+ info/0, info/1,
+ debug/1, socket_debug/1,
+ supports/0, supports/1, supports/2,
+ open/2, open/4,
+ bind/2, bind/3,
+ connect/1, connect/2,
+ listen/2,
+ accept/2,
+ send/4, sendto/5, sendmsg/4,
+ recv/4, recvfrom/4, recvmsg/5,
+ close/1, finalize_close/1,
+ shutdown/2, setopt/4, getopt/3,
+ sockname/1, peername/1,
+ cancel/3
+ ]).
+
+%% Also in socket
+-define(REGISTRY, socket_registry).
+
+
+%% ===========================================================================
+%%
+%% Defaults
+%%
+
+-define(ESOCK_SEND_FLAGS_DEFAULT, []).
+-define(ESOCK_SEND_TIMEOUT_DEFAULT, infinity).
+-define(ESOCK_SENDTO_FLAGS_DEFAULT, []).
+-define(ESOCK_SENDTO_TIMEOUT_DEFAULT, ?ESOCK_SEND_TIMEOUT_DEFAULT).
+-define(ESOCK_SENDMSG_FLAGS_DEFAULT, []).
+-define(ESOCK_SENDMSG_TIMEOUT_DEFAULT, ?ESOCK_SEND_TIMEOUT_DEFAULT).
+
+-define(ESOCK_RECV_FLAGS_DEFAULT, []).
+-define(ESOCK_RECV_TIMEOUT_DEFAULT, infinity).
+
+-define(ESOCK_SOCKADDR_IN4_DEFAULTS,
+ (#{port => 0, addr => any})).
+-define(ESOCK_SOCKADDR_IN6_DEFAULTS,
+ (#{port => 0, addr => any,
+ flowinfo => 0, scope_id => 0})).
+
+%% ===========================================================================
+%%
+%% Constants common to prim_socket_nif.c - has to be "identical"
+%%
+
+-define(ESOCK_SEND_FLAG_CONFIRM, (1 bsl 0)).
+-define(ESOCK_SEND_FLAG_DONTROUTE, (1 bsl 1)).
+-define(ESOCK_SEND_FLAG_EOR, (1 bsl 2)).
+-define(ESOCK_SEND_FLAG_MORE, (1 bsl 3)).
+-define(ESOCK_SEND_FLAG_NOSIGNAL, (1 bsl 4)).
+-define(ESOCK_SEND_FLAG_OOB, (1 bsl 5)).
+
+-define(ESOCK_RECV_FLAG_CMSG_CLOEXEC, (1 bsl 0)).
+-define(ESOCK_RECV_FLAG_ERRQUEUE, (1 bsl 1)).
+-define(ESOCK_RECV_FLAG_OOB, (1 bsl 2)).
+-define(ESOCK_RECV_FLAG_PEEK, (1 bsl 3)).
+-define(ESOCK_RECV_FLAG_TRUNC, (1 bsl 4)).
+
+
+%% shutdown/2
+-define(ESOCK_SHUTDOWN_HOW_READ, 0).
+-define(ESOCK_SHUTDOWN_HOW_WRITE, 1).
+-define(ESOCK_SHUTDOWN_HOW_READ_WRITE, 2).
+
+
+
+%% ----------------------------------
+%% Address domain / protcol family
+
+-define(ESOCK_DOMAIN_LOCAL, 1).
+-define(ESOCK_DOMAIN_UNIX, ?ESOCK_DOMAIN_LOCAL).
+-define(ESOCK_DOMAIN_INET, 2).
+-define(ESOCK_DOMAIN_INET6, 3).
+
+%% ----------------------------------
+%% Protocol type
+
+-define(ESOCK_TYPE_STREAM, 101).
+-define(ESOCK_TYPE_DGRAM, 102).
+-define(ESOCK_TYPE_RAW, 103).
+%% -define(ESOCK_TYPE_RDM, 104).
+-define(ESOCK_TYPE_SEQPACKET, 105).
+
+%% ----------------------------------
+%% Protocol
+
+-define(ESOCK_PROTOCOL_DEFAULT, 200).
+-define(ESOCK_PROTOCOL_IP, 201).
+-define(ESOCK_PROTOCOL_TCP, 202).
+-define(ESOCK_PROTOCOL_UDP, 203).
+-define(ESOCK_PROTOCOL_SCTP, 204).
+-define(ESOCK_PROTOCOL_ICMP, 205).
+-define(ESOCK_PROTOCOL_IGMP, 206).
+
+%% ----------------------------------
+%% Option level
+
+-define(ESOCK_OPT_LEVEL_OTP, 301).
+-define(ESOCK_OPT_LEVEL_SOCKET, 302).
+-define(ESOCK_OPT_LEVEL_IP, 303).
+-define(ESOCK_OPT_LEVEL_IPV6, 304).
+-define(ESOCK_OPT_LEVEL_TCP, 305).
+-define(ESOCK_OPT_LEVEL_UDP, 306).
+-define(ESOCK_OPT_LEVEL_SCTP, 307).
+
+%% ----------------------------------
+%% *** OTP (socket) options
+
+-define(ESOCK_OPT_OTP_DEBUG, 1001).
+-define(ESOCK_OPT_OTP_IOW, 1002).
+-define(ESOCK_OPT_OTP_CTRL_PROC, 1003).
+-define(ESOCK_OPT_OTP_RCVBUF, 1004).
+%%-define(ESOCK_OPT_OTP_SNDBUF, 1005).
+-define(ESOCK_OPT_OTP_RCVCTRLBUF, 1006).
+-define(ESOCK_OPT_OTP_SNDCTRLBUF, 1007).
+-define(ESOCK_OPT_OTP_FD, 1008).
+-define(ESOCK_OPT_OTP_META, 1009).
+%%
+-define(ESOCK_OPT_OTP_DOMAIN, 1999). % INTERNAL
+%%-define(ESOCK_OPT_OTP_TYPE, 1998). % INTERNAL
+%%-define(ESOCK_OPT_OTP_PROTOCOL, 1997). % INTERNAL
+%%-define(ESOCK_OPT_OTP_DTP, 1996). % INTERNAL
+
+%% ----------------------------------
+%% *** SOCKET (socket) options
+
+-define(ESOCK_OPT_SOCK_ACCEPTCONN, 2001).
+-define(ESOCK_OPT_SOCK_ACCEPTFILTER, 2002). % FreeBSD
+-define(ESOCK_OPT_SOCK_BINDTODEVICE, 2003).
+-define(ESOCK_OPT_SOCK_BROADCAST, 2004).
+-define(ESOCK_OPT_SOCK_BUSY_POLL, 2005).
+-define(ESOCK_OPT_SOCK_DEBUG, 2006).
+-define(ESOCK_OPT_SOCK_DOMAIN, 2007).
+-define(ESOCK_OPT_SOCK_DONTROUTE, 2008).
+-define(ESOCK_OPT_SOCK_ERROR, 2009).
+-define(ESOCK_OPT_SOCK_KEEPALIVE, 2010).
+-define(ESOCK_OPT_SOCK_LINGER, 2011).
+-define(ESOCK_OPT_SOCK_MARK, 2012).
+-define(ESOCK_OPT_SOCK_OOBINLINE, 2013).
+-define(ESOCK_OPT_SOCK_PASSCRED, 2014).
+-define(ESOCK_OPT_SOCK_PEEK_OFF, 2015).
+-define(ESOCK_OPT_SOCK_PEERCRED, 2016).
+-define(ESOCK_OPT_SOCK_PRIORITY, 2017).
+-define(ESOCK_OPT_SOCK_PROTOCOL, 2018).
+-define(ESOCK_OPT_SOCK_RCVBUF, 2019).
+-define(ESOCK_OPT_SOCK_RCVBUFFORCE, 2020).
+-define(ESOCK_OPT_SOCK_RCVLOWAT, 2021).
+-define(ESOCK_OPT_SOCK_RCVTIMEO, 2022).
+-define(ESOCK_OPT_SOCK_REUSEADDR, 2023).
+-define(ESOCK_OPT_SOCK_REUSEPORT, 2024).
+-define(ESOCK_OPT_SOCK_RXQ_OVFL, 2025).
+-define(ESOCK_OPT_SOCK_SETFIB, 2026). % FreeBSD
+-define(ESOCK_OPT_SOCK_SNDBUF, 2027).
+-define(ESOCK_OPT_SOCK_SNDBUFFORCE, 2028).
+-define(ESOCK_OPT_SOCK_SNDLOWAT, 2029).
+-define(ESOCK_OPT_SOCK_SNDTIMEO, 2030).
+-define(ESOCK_OPT_SOCK_TIMESTAMP, 2031).
+-define(ESOCK_OPT_SOCK_TYPE, 2032).
+
+%% ----------------------------------
+%% *** IP (socket) options
+
+-define(ESOCK_OPT_IP_ADD_MEMBERSHIP, 3001).
+-define(ESOCK_OPT_IP_ADD_SOURCE_MEMBERSHIP, 3002).
+-define(ESOCK_OPT_IP_BLOCK_SOURCE, 3003).
+-define(ESOCK_OPT_IP_DONTFRAG, 3004). % FreeBSD
+-define(ESOCK_OPT_IP_DROP_MEMBERSHIP, 3005).
+-define(ESOCK_OPT_IP_DROP_SOURCE_MEMBERSHIP,3006).
+-define(ESOCK_OPT_IP_FREEBIND, 3007).
+-define(ESOCK_OPT_IP_HDRINCL, 3008).
+-define(ESOCK_OPT_IP_MINTTL, 3009).
+-define(ESOCK_OPT_IP_MSFILTER, 3010).
+-define(ESOCK_OPT_IP_MTU, 3011).
+-define(ESOCK_OPT_IP_MTU_DISCOVER, 3012).
+-define(ESOCK_OPT_IP_MULTICAST_ALL, 3013).
+-define(ESOCK_OPT_IP_MULTICAST_IF, 3014).
+-define(ESOCK_OPT_IP_MULTICAST_LOOP, 3015).
+-define(ESOCK_OPT_IP_MULTICAST_TTL, 3016).
+-define(ESOCK_OPT_IP_NODEFRAG, 3017).
+-define(ESOCK_OPT_IP_OPTIONS, 3018). % FreeBSD
+-define(ESOCK_OPT_IP_PKTINFO, 3019).
+-define(ESOCK_OPT_IP_RECVDSTADDR, 3020). % FreeBSD
+-define(ESOCK_OPT_IP_RECVERR, 3021).
+-define(ESOCK_OPT_IP_RECVIF, 3022).
+-define(ESOCK_OPT_IP_RECVOPTS, 3023).
+-define(ESOCK_OPT_IP_RECVORIGDSTADDR, 3024).
+-define(ESOCK_OPT_IP_RECVTOS, 3025).
+-define(ESOCK_OPT_IP_RECVTTL, 3026).
+-define(ESOCK_OPT_IP_RETOPTS, 3027).
+-define(ESOCK_OPT_IP_ROUTER_ALERT, 3028).
+-define(ESOCK_OPT_IP_SENDSRCADDR, 3029). % FreeBSD
+-define(ESOCK_OPT_IP_TOS, 3030).
+-define(ESOCK_OPT_IP_TRANSPARENT, 3031).
+-define(ESOCK_OPT_IP_TTL, 3032).
+-define(ESOCK_OPT_IP_UNBLOCK_SOURCE, 3033).
+
+%% ----------------------------------
+%% *** IPv6 (socket) options
+
+-define(ESOCK_OPT_IPV6_ADDRFORM, 4001).
+-define(ESOCK_OPT_IPV6_ADD_MEMBERSHIP, 4002).
+-define(ESOCK_OPT_IPV6_AUTHHDR, 4003). % Obsolete?
+-define(ESOCK_OPT_IPV6_AUTH_LEVEL, 4004). % FreeBSD
+-define(ESOCK_OPT_IPV6_CHECKSUM, 4005). % FreeBSD
+-define(ESOCK_OPT_IPV6_DROP_MEMBERSHIP, 4006).
+-define(ESOCK_OPT_IPV6_DSTOPTS, 4007).
+-define(ESOCK_OPT_IPV6_ESP_NETWORK_LEVEL, 4008). % FreeBSD
+-define(ESOCK_OPT_IPV6_ESP_TRANS_LEVEL, 4009). % FreeBSD
+-define(ESOCK_OPT_IPV6_FAITH, 4010). % FreeBSD
+-define(ESOCK_OPT_IPV6_FLOWINFO, 4011).
+-define(ESOCK_OPT_IPV6_HOPLIMIT, 4012).
+-define(ESOCK_OPT_IPV6_HOPOPTS, 4013).
+-define(ESOCK_OPT_IPV6_IPCOMP_LEVEL, 4014). % FreeBSD
+-define(ESOCK_OPT_IPV6_JOIN_GROUP, 4015). % FreeBSD
+-define(ESOCK_OPT_IPV6_LEAVE_GROUP, 4016). % FreeBSD
+-define(ESOCK_OPT_IPV6_MTU, 4017).
+-define(ESOCK_OPT_IPV6_MTU_DISCOVER, 4018).
+-define(ESOCK_OPT_IPV6_MULTICAST_HOPS, 4019).
+-define(ESOCK_OPT_IPV6_MULTICAST_IF, 4020).
+-define(ESOCK_OPT_IPV6_MULTICAST_LOOP, 4021).
+-define(ESOCK_OPT_IPV6_PORTRANGE, 4022). % FreeBSD
+-define(ESOCK_OPT_IPV6_PKTOPTIONS, 4023). % FreeBSD
+-define(ESOCK_OPT_IPV6_RECVERR, 4024).
+-define(ESOCK_OPT_IPV6_RECVHOPLIMIT, 4025).
+-define(ESOCK_OPT_IPV6_RECVPKTINFO, 4026). % On FreeBSD: PKTINFO
+-define(ESOCK_OPT_IPV6_RECVTCLASS, 4027).
+-define(ESOCK_OPT_IPV6_ROUTER_ALERT, 4028).
+-define(ESOCK_OPT_IPV6_RTHDR, 4029).
+-define(ESOCK_OPT_IPV6_TCLASS, 4030). % FreeBSD
+-define(ESOCK_OPT_IPV6_UNICAST_HOPS, 4031).
+-define(ESOCK_OPT_IPV6_USE_MIN_MTU, 4032). % FreeBSD
+-define(ESOCK_OPT_IPV6_V6ONLY, 4033).
+
+%% ----------------------------------
+%% *** TCP (socket) options
+
+-define(ESOCK_OPT_TCP_CONGESTION, 5001).
+-define(ESOCK_OPT_TCP_CORK, 5002).
+-define(ESOCK_OPT_TCP_INFO, 5003).
+-define(ESOCK_OPT_TCP_KEEPCNT, 5004).
+-define(ESOCK_OPT_TCP_KEEPIDLE, 5005).
+-define(ESOCK_OPT_TCP_KEEPINTVL, 5006).
+-define(ESOCK_OPT_TCP_MAXSEG, 5007).
+-define(ESOCK_OPT_TCP_MD5SIG, 5008).
+-define(ESOCK_OPT_TCP_NODELAY, 5009).
+-define(ESOCK_OPT_TCP_NOOPT, 5010).
+-define(ESOCK_OPT_TCP_NOPUSH, 5011).
+-define(ESOCK_OPT_TCP_SYNCNT, 5012).
+-define(ESOCK_OPT_TCP_USER_TIMEOUT, 5013).
+
+%% ----------------------------------
+%% *** UDP (socket) options
+
+-define(ESOCK_OPT_UDP_CORK, 6001).
+
+%% ----------------------------------
+%% *** SCTP (socket) options
+
+-define(ESOCK_OPT_SCTP_ADAPTION_LAYER, 7001).
+-define(ESOCK_OPT_SCTP_ASSOCINFO, 7002).
+-define(ESOCK_OPT_SCTP_AUTH_ACTIVE_KEY, 7003).
+-define(ESOCK_OPT_SCTP_AUTH_ASCONF, 7004).
+-define(ESOCK_OPT_SCTP_AUTH_CHUNK, 7005).
+-define(ESOCK_OPT_SCTP_AUTH_KEY, 7006).
+-define(ESOCK_OPT_SCTP_AUTH_DELETE_KEY, 7007).
+-define(ESOCK_OPT_SCTP_AUTOCLOSE, 7008).
+-define(ESOCK_OPT_SCTP_CONTEXT, 7009).
+-define(ESOCK_OPT_SCTP_DEFAULT_SEND_PARAMS, 7010).
+-define(ESOCK_OPT_SCTP_DELAYED_ACK_TIME, 7011).
+-define(ESOCK_OPT_SCTP_DISABLE_FRAGMENTS, 7012).
+-define(ESOCK_OPT_SCTP_HMAC_IDENT, 7013).
+-define(ESOCK_OPT_SCTP_EVENTS, 7014).
+-define(ESOCK_OPT_SCTP_EXPLICIT_EOR, 7015).
+-define(ESOCK_OPT_SCTP_FRAGMENT_INTERLEAVE, 7016).
+-define(ESOCK_OPT_SCTP_GET_PEER_ADDR_INFO, 7017).
+-define(ESOCK_OPT_SCTP_INITMSG, 7018).
+-define(ESOCK_OPT_SCTP_I_WANT_MAPPED_V4_ADDR, 7019).
+-define(ESOCK_OPT_SCTP_LOCAL_AUTH_CHUNKS, 7020).
+-define(ESOCK_OPT_SCTP_MAXSEG, 7021).
+-define(ESOCK_OPT_SCTP_MAXBURST, 7022).
+-define(ESOCK_OPT_SCTP_NODELAY, 7023).
+-define(ESOCK_OPT_SCTP_PARTIAL_DELIVERY_POINT, 7024).
+-define(ESOCK_OPT_SCTP_PEER_ADDR_PARAMS, 7025).
+-define(ESOCK_OPT_SCTP_PEER_AUTH_CHUNKS, 7026).
+-define(ESOCK_OPT_SCTP_PRIMARY_ADDR, 7027).
+-define(ESOCK_OPT_SCTP_RESET_STREAMS, 7028).
+-define(ESOCK_OPT_SCTP_RTOINFO, 7029).
+-define(ESOCK_OPT_SCTP_SET_PEER_PRIMARY_ADDR, 7030).
+-define(ESOCK_OPT_SCTP_STATUS, 7031).
+-define(ESOCK_OPT_SCTP_USE_EXT_RECVINFO, 7032).
+
+
+%% ===========================================================================
+%%
+%% Guard macros
+%%
+
+%% Check that there are 1:s just in the lowest 8 bits of all 4 elements
+-define(
+ IS_IPV4_ADDR(A),
+ (is_tuple(A) andalso tuple_size(A) =:= 4
+ andalso
+ ((element(1, (A)) bor element(2, (A))
+ bor element(3, (A)) bor element(4, (A))
+ ) band (bnot 16#FF)
+ ) =:= 0)).
+
+%% Check that there are 1:s just in the lowest 16 bits of all 8 elements
+-define(
+ IS_IPV6_ADDR(A),
+ (is_tuple(A) andalso tuple_size(A) =:= 8
+ andalso
+ ((element(1, (A)) bor element(2, (A))
+ bor element(3, (A)) bor element(4, (A))
+ bor element(5, (A)) bor element(6, (A))
+ bor element(7, (A)) bor element(8, (A))
+ ) band (bnot 16#FFFF)
+ ) =:= 0)).
+
+%% ===========================================================================
+%% API for 'erl_init'
+%%
+
+on_load() ->
+ on_load(#{}).
+
+on_load(Extra) when is_map(Extra) ->
+ %% This is spawned as a system process to prevent init:restart/0 from
+ %% killing it.
+ Pid = erts_internal:spawn_system_process(?REGISTRY, start, []),
+ DebugFilename =
+ case os:get_env_var("ESOCK_DEBUG_FILENAME") of
+ "*" ->
+ "/tmp/esock-dbg-??????";
+ F ->
+ F
+ end,
+ ok =
+ erlang:load_nif(
+ atom_to_list(?MODULE),
+ case DebugFilename of
+ false ->
+ Extra#{registry => Pid};
+ _ ->
+ Extra
+ #{registry => Pid,
+ debug => true,
+ socket_debug => true,
+ debug_filename =>
+ encode_path(DebugFilename)}
+ end).
+
+%% ===========================================================================
+%% API for 'socket'
+%%
+
+%% File names has to be encoded according to
+%% the native file encoding
+%%
+encode_path(Path) ->
+ %% These are all BIFs - will not cause code loading
+ unicode:characters_to_binary(Path, file:native_name_encoding()).
+
+encode_sockaddr(SockAddr) ->
+ enc_sockaddr(SockAddr).
+
+%% ----------------------------------
+
+info() ->
+ nif_info().
+
+info(SockRef) ->
+ nif_info(SockRef).
+
+%% ----------------------------------
+
+debug(D) ->
+ nif_command(#{command => ?FUNCTION_NAME, data => D}).
+
+
+socket_debug(D) ->
+ nif_command(#{command => ?FUNCTION_NAME, data => D}).
+
+%% ----------------------------------
+
+supports() ->
+ nif_supports().
+
+supports(Key) ->
+ nif_supports(Key).
+
+supports(options = Key, Level) ->
+ case Level of
+ socket ->
+ nif_supports(Key, ?ESOCK_OPT_LEVEL_SOCKET);
+ ip ->
+ nif_supports(Key, ?ESOCK_OPT_LEVEL_IP);
+ ipv6 ->
+ nif_supports(Key, ?ESOCK_OPT_LEVEL_IPV6);
+ tcp ->
+ nif_supports(Key, ?ESOCK_OPT_LEVEL_TCP);
+ udp ->
+ nif_supports(Key, ?ESOCK_OPT_LEVEL_UDP);
+ sctp ->
+ nif_supports(Key, ?ESOCK_OPT_LEVEL_SCTP);
+ _ ->
+ []
+ end;
+supports(_Key1, _Key2) ->
+ [].
+
+%% ----------------------------------
+
+open(FD, Opts) when is_map(Opts) ->
+ EOpts =
+ maps:map(
+ fun (Key, Val) ->
+ case Key of
+ domain ->
+ enc_domain(Val);
+ type ->
+ enc_type(Val);
+ protocol ->
+ enc_protocol(Val);
+ _ ->
+ Val
+ end
+ end, Opts),
+ nif_open(FD, EOpts).
+
+open(Domain, Type, Protocol, Opts) when is_map(Opts) ->
+ EDomain = enc_domain(Domain),
+ EType = enc_type(Type),
+ EProtocol = enc_protocol(Protocol),
+ EOpts =
+ case Opts of
+ #{netns := Path} when is_list(Path) ->
+ Opts#{netns := encode_path(Path)};
+ _ ->
+ Opts
+ end,
+ nif_open(EDomain, EType, EProtocol, EOpts).
+
+%% ----------------------------------
+
+bind(SockRef, Addr) ->
+ nif_bind(SockRef, enc_sockaddr(Addr)).
+
+bind(SockRef, Addrs, Action) when is_list(Addrs) ->
+ EAddrs = [enc_sockaddr(Addr) || Addr <- Addrs],
+ nif_bind(SockRef, EAddrs, Action).
+
+%% ----------------------------------
+
+connect(SockRef, SockAddr) ->
+ nif_connect(SockRef, enc_sockaddr(SockAddr)).
+
+connect(SockRef) ->
+ nif_connect(SockRef).
+
+%% ----------------------------------
+
+listen(SockRef, Backlog) ->
+ nif_listen(SockRef, Backlog).
+
+%% ----------------------------------
+
+accept(ListenSockRef, AccRef) ->
+ case nif_accept(ListenSockRef, AccRef) of
+ {ok, _SockRef} = Result ->
+ Result;
+ {error, eagain} ->
+ select;
+ {error, _} = Result ->
+ Result
+ end.
+
+%% ----------------------------------
+
+send(SockRef, SendRef, Data, Flags) ->
+ EFlags = enc_send_flags(Flags),
+ send_result(
+ nif_send(SockRef, SendRef, Data, EFlags)).
+
+sendto(SockRef, SendRef, Data, To, Flags) ->
+ ETo = enc_sockaddr(To),
+ EFlags = enc_send_flags(Flags),
+ send_result(
+ nif_sendto(SockRef, SendRef, Data, ETo, EFlags)).
+
+sendmsg(SockRef, SendRef, MsgHdr, Flags) ->
+ EMsgHdr = enc_msghdr(MsgHdr),
+ EFlags = enc_send_flags(Flags),
+ send_result(
+ nif_sendmsg(SockRef, SendRef, EMsgHdr, EFlags)).
+
+send_result(Result) ->
+ case Result of
+ ok -> ok;
+ {ok, _Written} = OK -> OK;
+ {error, eagain} -> select;
+ {error, _} = ERROR -> ERROR
+ end.
+
+%% ----------------------------------
+
+recv(SockRef, RecvRef, Length, Flags) ->
+ EFlags = enc_recv_flags(Flags),
+ case nif_recv(SockRef, RecvRef, Length, EFlags) of
+ {ok, true, Bin} ->
+ {ok, Bin};
+ %%
+ {ok, false, Bin} ->
+ %% Depending on the number of bytes we tried to read:
+ if
+ Length =:= 0 ->
+ %% 0 - Read everything available
+ %% We got something, but there may be more
+ %% - keep reading.
+ {more, Bin};
+ true ->
+ %% > 0 - We got a part of the message
+ %% and we will be notified when there is more to read
+ %% (a select message)
+ {select, Bin}
+ end;
+ %%
+ {error, eagain} ->
+ select;
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+recvfrom(SockRef, RecvRef, Length, Flags) ->
+ EFlags = enc_recv_flags(Flags),
+ recvfromsg_result(
+ nif_recvfrom(SockRef, RecvRef, Length, EFlags)).
+
+recvmsg(SockRef, RecvRef, BufSz, CtrlSz, Flags) ->
+ EFlags = enc_recv_flags(Flags),
+ recvfromsg_result(
+ nif_recvmsg(SockRef, RecvRef, BufSz, CtrlSz, EFlags)).
+
+recvfromsg_result(Result) ->
+ case Result of
+ {ok, _} = OK -> OK;
+ {error, eagain} -> select;
+ {error, _} = ERROR -> ERROR
+ end.
+
+%% ----------------------------------
+
+close(SockRef) ->
+ nif_close(SockRef).
+
+finalize_close(SockRef) ->
+ nif_finalize_close(SockRef).
+
+%% ----------------------------------
+
+shutdown(SockRef, How) ->
+ nif_shutdown(SockRef, enc_shutdown_how(How)).
+
+%% ----------------------------------
+
+setopt(SockRef, Level, Key, Val)
+ when is_integer(Level), is_integer(Key), is_binary(Val) ->
+ nif_setopt(SockRef, false, Level, Key, Val);
+setopt(SockRef, Level, Key, Value) ->
+ case enc_sockopt_type(Level, Key) of
+ {undefined, _ELevel, _EKey} ->
+ {error, einval};
+ {Type, ELevel, EKey} ->
+ EValue = enc_setopt_value(Level, Key, Value, Type),
+ nif_setopt(SockRef, true, ELevel, EKey, EValue)
+ end.
+
+getopt(SockRef, Level, Key)
+ when is_integer(Level) ->
+ nif_getopt(SockRef, false, Level, Key);
+getopt(SockRef, Level, Key) ->
+ {_Type, ELevel, EKey} = enc_sockopt_type(Level, Key),
+ case nif_getopt(SockRef, true, ELevel, EKey) of
+ {ok, Value} ->
+ {ok, dec_getopt_value(Value, Level, Key)};
+ {error, _} = Error ->
+ Error
+ end.
+
+%% ----------------------------------
+
+sockname(Ref) ->
+ nif_sockname(Ref).
+
+peername(Ref) ->
+ nif_peername(Ref).
+
+%% ----------------------------------
+
+cancel(SRef, Op, Ref) ->
+ nif_cancel(SRef, Op, Ref).
+
+%% ===========================================================================
+%% Encode / decode
+%%
+
+enc_domain(local) -> ?ESOCK_DOMAIN_LOCAL;
+enc_domain(inet) -> ?ESOCK_DOMAIN_INET;
+enc_domain(inet6) -> ?ESOCK_DOMAIN_INET6;
+enc_domain(Domain) -> invalid(domain, Domain).
+
+enc_type(stream) -> ?ESOCK_TYPE_STREAM;
+enc_type(dgram) -> ?ESOCK_TYPE_DGRAM;
+enc_type(raw) -> ?ESOCK_TYPE_RAW;
+enc_type(seqpacket) -> ?ESOCK_TYPE_SEQPACKET;
+enc_type(Type) -> invalid(type, Type).
+
+enc_protocol(default) -> ?ESOCK_PROTOCOL_DEFAULT;
+enc_protocol(ip) -> ?ESOCK_PROTOCOL_IP;
+enc_protocol(tcp) -> ?ESOCK_PROTOCOL_TCP;
+enc_protocol(udp) -> ?ESOCK_PROTOCOL_UDP;
+enc_protocol(sctp) -> ?ESOCK_PROTOCOL_SCTP;
+enc_protocol(icmp) -> ?ESOCK_PROTOCOL_ICMP;
+enc_protocol(igmp) -> ?ESOCK_PROTOCOL_IGMP;
+enc_protocol({raw, P} = RAW) when is_integer(P) -> RAW;
+enc_protocol(Proto) ->
+ invalid(protocol, Proto).
+
+
+enc_sockaddr(#{family := inet} = SockAddr) ->
+ maps:merge(?ESOCK_SOCKADDR_IN4_DEFAULTS, SockAddr);
+enc_sockaddr(#{family := inet6} = SockAddr) ->
+ maps:merge(?ESOCK_SOCKADDR_IN6_DEFAULTS, SockAddr);
+enc_sockaddr(#{family := local, path := Path} = SockAddr)
+ when is_list(Path) andalso
+ (length(Path) > 0) andalso
+ (length(Path) =< 255) ->
+ BinPath = encode_path(Path),
+ enc_sockaddr(SockAddr#{path => BinPath});
+enc_sockaddr(#{family := local, path := Path} = SockAddr)
+ when is_binary(Path) andalso
+ (byte_size(Path) > 0) andalso
+ (byte_size(Path) =< 255) ->
+ SockAddr;
+enc_sockaddr(SockAddr) ->
+ invalid(address, SockAddr).
+
+
+enc_send_flags([]) -> 0;
+enc_send_flags([Flag | Flags]) ->
+ case Flag of
+ confirm -> ?ESOCK_SEND_FLAG_CONFIRM;
+ dontroute -> ?ESOCK_SEND_FLAG_DONTROUTE;
+ eor -> ?ESOCK_SEND_FLAG_EOR;
+ more -> ?ESOCK_SEND_FLAG_MORE;
+ nosignal -> ?ESOCK_SEND_FLAG_NOSIGNAL;
+ oob -> ?ESOCK_SEND_FLAG_OOB;
+ _ -> invalid(flag, Flag)
+ end bor enc_send_flags(Flags).
+
+
+enc_recv_flags([]) -> 0;
+enc_recv_flags([Flag | Flags]) ->
+ case Flag of
+ cmsg_cloexec -> ?ESOCK_RECV_FLAG_CMSG_CLOEXEC;
+ errqueue -> ?ESOCK_RECV_FLAG_ERRQUEUE;
+ oob -> ?ESOCK_RECV_FLAG_OOB;
+ peek -> ?ESOCK_RECV_FLAG_PEEK;
+ trunc -> ?ESOCK_RECV_FLAG_TRUNC;
+ _ -> invalid(flag, Flag)
+ end bor enc_recv_flags(Flags).
+
+
+enc_msghdr(#{ctrl := []} = M) ->
+ enc_msghdr(maps:remove(ctrl, M));
+enc_msghdr(#{iov := IOV, addr := Addr} = M)
+ when is_list(IOV), IOV =/= [] ->
+ M#{iov => erlang:iolist_to_iovec(IOV),
+ addr => enc_sockaddr(Addr)};
+enc_msghdr(#{iov := IOV} = M)
+ when is_list(IOV), IOV =/= [] ->
+ M#{iov => erlang:iolist_to_iovec(IOV)};
+enc_msghdr(#{} = M) ->
+ not_supported(M).
+
+
+%% Common to setopt and getopt
+%% - the returned first tuple element is a type tag
+%% that is used by setopt and ignored by getopt;
+%% 'undefined' means setopt encoding is not_supported
+enc_sockopt_type(otp = Level, Key) ->
+ L = ?ESOCK_OPT_LEVEL_OTP,
+ case Key of
+ debug -> {boolean, L, ?ESOCK_OPT_OTP_DEBUG};
+ iow -> {boolean, L, ?ESOCK_OPT_OTP_IOW};
+ controlling_process -> {pid, L, ?ESOCK_OPT_OTP_CTRL_PROC};
+ rcvbuf -> {Key, L, ?ESOCK_OPT_OTP_RCVBUF};
+ rcvctrlbuf -> {buf, L, ?ESOCK_OPT_OTP_RCVCTRLBUF};
+ sndctrlbuf -> {buf, L, ?ESOCK_OPT_OTP_SNDCTRLBUF};
+ fd -> {undefined, L, ?ESOCK_OPT_OTP_FD};
+ meta -> {term, L, ?ESOCK_OPT_OTP_META};
+ domain -> {undefined, L, ?ESOCK_OPT_OTP_DOMAIN};
+ _ ->
+ not_supported(Level, Key)
+ end;
+enc_sockopt_type(socket = Level, Key) ->
+ L = ?ESOCK_OPT_LEVEL_SOCKET,
+ case Key of
+ acceptconn -> {undefined, L, ?ESOCK_OPT_SOCK_ACCEPTCONN};
+ acceptfilter -> {undefined, L, ?ESOCK_OPT_SOCK_ACCEPTFILTER};
+ bindtodevice ->
+ %% Before linux 3.8, this socket option
+ %% could be set but not get.
+ %% Maximum size of buffer for name: IFNAMSIZ
+ %% So, we let the implementation decide.
+ {list, L, ?ESOCK_OPT_SOCK_BINDTODEVICE};
+ broadcast -> {boolean, L, ?ESOCK_OPT_SOCK_BROADCAST};
+ busy_poll -> {undefined, L, ?ESOCK_OPT_SOCK_BUSY_POLL};
+ debug -> {integer, L, ?ESOCK_OPT_SOCK_DEBUG};
+ domain -> {undefined, L, ?ESOCK_OPT_SOCK_DOMAIN};
+ dontroute -> {boolean, L, ?ESOCK_OPT_SOCK_DONTROUTE};
+ error -> {undefined, L, ?ESOCK_OPT_SOCK_ERROR};
+ keepalive -> {boolean, L, ?ESOCK_OPT_SOCK_KEEPALIVE};
+ linger -> {linger, L, ?ESOCK_OPT_SOCK_LINGER};
+ mark -> {undefined, L, ?ESOCK_OPT_SOCK_MARK};
+ oobinline -> {boolean, L, ?ESOCK_OPT_SOCK_OOBINLINE};
+ passcred -> {boolean, L, ?ESOCK_OPT_SOCK_PASSCRED};
+ peek_off -> {integer, L, ?ESOCK_OPT_SOCK_PEEK_OFF};
+ peercred -> {undefined, L, ?ESOCK_OPT_SOCK_PEERCRED};
+ priority -> {integer, L, ?ESOCK_OPT_SOCK_PRIORITY};
+ protocol -> {undefined, L, ?ESOCK_OPT_SOCK_PROTOCOL};
+ rcvbuf -> {integer, L, ?ESOCK_OPT_SOCK_RCVBUF};
+ rcvbufforce -> {undefined, L, ?ESOCK_OPT_SOCK_RCVBUFFORCE};
+ rcvlowat ->
+ %% May not work on Linux
+ {integer, L, ?ESOCK_OPT_SOCK_RCVLOWAT};
+ rcvtimeo -> {timeval, L, ?ESOCK_OPT_SOCK_RCVTIMEO};
+ reuseaddr -> {boolean, L, ?ESOCK_OPT_SOCK_REUSEADDR};
+ reuseport -> {boolean, L, ?ESOCK_OPT_SOCK_REUSEPORT};
+ rxq_ovfl -> {undefined, L, ?ESOCK_OPT_SOCK_RXQ_OVFL};
+ setfib -> {undefined, L, ?ESOCK_OPT_SOCK_SETFIB};
+ sndbuf -> {integer, L, ?ESOCK_OPT_SOCK_SNDBUF};
+ sndbufforce -> {undefined, L, ?ESOCK_OPT_SOCK_SNDBUFFORCE};
+ sndlowat ->
+ %% Not changeable on Linux
+ {integer, L, ?ESOCK_OPT_SOCK_SNDLOWAT};
+ sndtimeo -> {timeval, L, ?ESOCK_OPT_SOCK_SNDTIMEO};
+ timestamp -> {boolean, L, ?ESOCK_OPT_SOCK_TIMESTAMP};
+ type -> {undefined, L, ?ESOCK_OPT_SOCK_TYPE};
+ _ ->
+ not_supported(Level, Key)
+ end;
+enc_sockopt_type(ip = Level, Key) ->
+ L = ?ESOCK_OPT_LEVEL_IP,
+ case Key of
+ add_membership ->
+ {addr_if, L, ?ESOCK_OPT_IP_ADD_MEMBERSHIP};
+ add_source_membership ->
+ {addr_if_src, L, ?ESOCK_OPT_IP_ADD_SOURCE_MEMBERSHIP};
+ block_source ->
+ {addr_if_src, L, ?ESOCK_OPT_IP_BLOCK_SOURCE};
+ dontfrag ->
+ %% FreeBSD only?
+ %% Only respected on udp and raw ip
+ %% (unless the hdrincl option has been set)
+ {boolean, L, ?ESOCK_OPT_IP_DONTFRAG};
+ drop_membership ->
+ {addr_if, L, ?ESOCK_OPT_IP_DROP_MEMBERSHIP};
+ drop_source_membership ->
+ {addr_if_src, L, ?ESOCK_OPT_IP_DROP_SOURCE_MEMBERSHIP};
+ freebind ->
+ %% Linux only?
+ {boolean, L, ?ESOCK_OPT_IP_FREEBIND};
+ hdrincl ->
+ {boolean, L, ?ESOCK_OPT_IP_HDRINCL};
+ minttl ->
+ {integer, L, ?ESOCK_OPT_IP_MINTTL};
+ msfilter -> {Key, L, ?ESOCK_OPT_IP_MSFILTER};
+ mtu -> {undefined, L, ?ESOCK_OPT_IP_MTU};
+ mtu_discover -> {Key, L, ?ESOCK_OPT_IP_MTU_DISCOVER};
+ multicast_all ->
+ {boolean, L, ?ESOCK_OPT_IP_MULTICAST_ALL};
+ multicast_if ->
+ {boolean, L, ?ESOCK_OPT_IP_MULTICAST_ALL};
+ multicast_loop ->
+ {boolean, L, ?ESOCK_OPT_IP_MULTICAST_LOOP};
+ multicast_ttl ->
+ {byte, L, ?ESOCK_OPT_IP_MULTICAST_TTL};
+ nodefrag ->
+ {boolean, L, ?ESOCK_OPT_IP_NODEFRAG};
+ options ->
+ {undefined, L, ?ESOCK_OPT_IP_OPTIONS};
+ pktinfo ->
+ {boolean, L, ?ESOCK_OPT_IP_PKTINFO};
+ recvdstaddr ->
+ {boolean, L, ?ESOCK_OPT_IP_RECVDSTADDR};
+ recverr ->
+ {boolean, L, ?ESOCK_OPT_IP_RECVERR};
+ recvif ->
+ {boolean, L, ?ESOCK_OPT_IP_RECVIF};
+ recvopts ->
+ {boolean, L, ?ESOCK_OPT_IP_RECVOPTS};
+ recvorigdstaddr ->
+ {boolean, L, ?ESOCK_OPT_IP_RECVORIGDSTADDR};
+ recvtos ->
+ {boolean, L, ?ESOCK_OPT_IP_RECVTOS};
+ recvttl ->
+ {boolean, L, ?ESOCK_OPT_IP_RECVTTL};
+ retopts ->
+ {boolean, L, ?ESOCK_OPT_IP_RETOPTS};
+ router_alert ->
+ {integer, L, ?ESOCK_OPT_IP_ROUTER_ALERT};
+ sendsrcaddr ->
+ {boolean, L, ?ESOCK_OPT_IP_SENDSRCADDR};
+ tos ->
+ %% 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)...
+ {Key, L, ?ESOCK_OPT_IP_TOS};
+ transparent ->
+ {boolean, L, ?ESOCK_OPT_IP_TRANSPARENT};
+ ttl ->
+ {integer, L, ?ESOCK_OPT_IP_TTL};
+ unblock_source ->
+ {addr_if_src, L, ?ESOCK_OPT_IP_UNBLOCK_SOURCE};
+ _ ->
+ not_supported(Level, Key)
+ end;
+enc_sockopt_type(ipv6 = Level, Key) ->
+ L = ?ESOCK_OPT_LEVEL_IPV6,
+ case Key of
+ addrform -> {Key, L, ?ESOCK_OPT_IPV6_ADDRFORM};
+ add_membership ->
+ {addr_if, L, ?ESOCK_OPT_IPV6_ADD_MEMBERSHIP};
+ authhdr ->
+ %% 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...
+ {boolean, L, ?ESOCK_OPT_IPV6_AUTHHDR};
+ auth_level ->
+ {undefined, L, ?ESOCK_OPT_IPV6_AUTH_LEVEL};
+ checksum ->
+ {undefined, L, ?ESOCK_OPT_IPV6_CHECKSUM};
+ drop_membership ->
+ {addr_if, L, ?ESOCK_OPT_IPV6_DROP_MEMBERSHIP};
+ dstopts ->
+ {boolean, L, ?ESOCK_OPT_IPV6_DSTOPTS};
+ esp_network_level ->
+ {undefined, L, ?ESOCK_OPT_IPV6_ESP_NETWORK_LEVEL};
+ esp_trans_level ->
+ {undefined, L, ?ESOCK_OPT_IPV6_ESP_TRANS_LEVEL};
+ flowinfo ->
+ {boolean, L, ?ESOCK_OPT_IPV6_FLOWINFO};
+ hoplimit ->
+ {boolean, L, ?ESOCK_OPT_IPV6_HOPLIMIT};
+ hopopts ->
+ {boolean, L, ?ESOCK_OPT_IPV6_HOPOPTS};
+ ipcomp_level ->
+ {undefined, L, ?ESOCK_OPT_IPV6_IPCOMP_LEVEL};
+ join_group ->
+ {undefined, L, ?ESOCK_OPT_IPV6_JOIN_GROUP};
+ leave_group ->
+ {undefined, L, ?ESOCK_OPT_IPV6_LEAVE_GROUP};
+ mtu ->
+ {integer, L, ?ESOCK_OPT_IPV6_MTU};
+ mtu_discover -> {Key, L, ?ESOCK_OPT_IPV6_MTU_DISCOVER};
+ multicast_hops ->
+ {hops, L, ?ESOCK_OPT_IPV6_MULTICAST_HOPS};
+ multicast_if ->
+ {integer, L, ?ESOCK_OPT_IPV6_MULTICAST_IF};
+ multicast_loop ->
+ {boolean, L, ?ESOCK_OPT_IPV6_MULTICAST_LOOP};
+ portrange ->
+ {undefined, L, ?ESOCK_OPT_IPV6_PORTRANGE};
+ pktoptions ->
+ {undefined, L, ?ESOCK_OPT_IPV6_PKTOPTIONS};
+ recverr ->
+ {boolean, L, ?ESOCK_OPT_IPV6_RECVERR};
+ recvhoplimit ->
+ {boolean, L, ?ESOCK_OPT_IPV6_RECVHOPLIMIT};
+ recvpktinfo ->
+ {boolean, L, ?ESOCK_OPT_IPV6_RECVPKTINFO};
+ pktinfo -> % alias on FreeBSD
+ {boolean, L, ?ESOCK_OPT_IPV6_RECVPKTINFO};
+ recvtclass ->
+ {boolean, L, ?ESOCK_OPT_IPV6_RECVTCLASS};
+ router_alert ->
+ {integer, L, ?ESOCK_OPT_IPV6_ROUTER_ALERT};
+ rthdr ->
+ {boolean, L, ?ESOCK_OPT_IPV6_RTHDR};
+ tclass ->
+ {boolean, L, ?ESOCK_OPT_IPV6_TCLASS};
+ unicast_hops ->
+ {hops, L, ?ESOCK_OPT_IPV6_UNICAST_HOPS};
+ use_min_mtu ->
+ {undefined, L, ?ESOCK_OPT_IPV6_USE_MIN_MTU};
+ v6only ->
+ {boolean, L, ?ESOCK_OPT_IPV6_V6ONLY};
+ _ ->
+ not_supported(Level, Key)
+ end;
+enc_sockopt_type(tcp = Level, Key) ->
+ L = ?ESOCK_OPT_LEVEL_TCP,
+ case Key of
+ congestion ->
+ {list, L, ?ESOCK_OPT_TCP_CONGESTION};
+ cork ->
+ {boolean, L, ?ESOCK_OPT_TCP_CORK};
+ info ->
+ {undefined, L, ?ESOCK_OPT_TCP_INFO};
+ keepcnt ->
+ {undefined, L, ?ESOCK_OPT_TCP_KEEPCNT};
+ keepidle ->
+ {undefined, L, ?ESOCK_OPT_TCP_KEEPIDLE};
+ keepintvl ->
+ {undefined, L, ?ESOCK_OPT_TCP_KEEPINTVL};
+ maxseg ->
+ {integer, L, ?ESOCK_OPT_TCP_MAXSEG};
+ md5seg ->
+ {undefined, L, ?ESOCK_OPT_TCP_MD5SIG};
+ nodelay ->
+ {boolean, L, ?ESOCK_OPT_TCP_NODELAY};
+ noopt ->
+ {undefined, L, ?ESOCK_OPT_TCP_NOOPT};
+ nopush ->
+ {undefined, L, ?ESOCK_OPT_TCP_NOPUSH};
+ syncnt ->
+ %% Only set? 1..255
+ {undefined, L, ?ESOCK_OPT_TCP_SYNCNT};
+ user_timeout ->
+ {undefined, L, ?ESOCK_OPT_TCP_USER_TIMEOUT};
+ _ ->
+ not_supported(Level, Key)
+ end;
+enc_sockopt_type(udp = Level, Key) ->
+ L = ?ESOCK_OPT_LEVEL_UDP,
+ case Key of
+ cork ->
+ {boolean, L, ?ESOCK_OPT_UDP_CORK};
+ _ ->
+ not_supported(Level, Key)
+ end;
+enc_sockopt_type(sctp = Level, Key) ->
+ L = ?ESOCK_OPT_LEVEL_SCTP,
+ case Key of
+ adaption_layer ->
+ {undefined, L, ?ESOCK_OPT_SCTP_ADAPTION_LAYER};
+ associnfo -> {Key, L, ?ESOCK_OPT_SCTP_ASSOCINFO};
+ auth_active_key ->
+ {undefined, L, ?ESOCK_OPT_SCTP_AUTH_ACTIVE_KEY};
+ auth_asconf ->
+ {undefined, L, ?ESOCK_OPT_SCTP_AUTH_ASCONF};
+ auth_chunk ->
+ {undefined, L, ?ESOCK_OPT_SCTP_AUTH_CHUNK};
+ auth_key ->
+ {undefined, L, ?ESOCK_OPT_SCTP_AUTH_KEY};
+ auth_delete_key ->
+ {undefined, L, ?ESOCK_OPT_SCTP_AUTH_DELETE_KEY};
+ autoclose ->
+ {integer, L, ?ESOCK_OPT_SCTP_AUTOCLOSE};
+ context ->
+ {undefined, L, ?ESOCK_OPT_SCTP_CONTEXT};
+ default_send_params ->
+ {undefined, L, ?ESOCK_OPT_SCTP_DEFAULT_SEND_PARAMS};
+ delayed_ack_time ->
+ {undefined, L, ?ESOCK_OPT_SCTP_DELAYED_ACK_TIME};
+ disable_fragments ->
+ {boolean, L, ?ESOCK_OPT_SCTP_DISABLE_FRAGMENTS};
+ hmac_ident ->
+ {undefined, L, ?ESOCK_OPT_SCTP_HMAC_IDENT};
+ events -> {Key, L, ?ESOCK_OPT_SCTP_EVENTS};
+ explicit_eor ->
+ {undefined, L, ?ESOCK_OPT_SCTP_EXPLICIT_EOR};
+ fragment_intreleave ->
+ {undefined, L, ?ESOCK_OPT_SCTP_FRAGMENT_INTERLEAVE};
+ get_peer_addr_info ->
+ {undefined, L, ?ESOCK_OPT_SCTP_GET_PEER_ADDR_INFO};
+ initmsg -> {Key, L, ?ESOCK_OPT_SCTP_INITMSG};
+ i_want_mapped_v4_addr ->
+ {undefined, L, ?ESOCK_OPT_SCTP_I_WANT_MAPPED_V4_ADDR};
+ local_auth_chunks ->
+ {undefined, L, ?ESOCK_OPT_SCTP_LOCAL_AUTH_CHUNKS};
+ maxseg ->
+ {integer, L, ?ESOCK_OPT_SCTP_MAXSEG};
+ maxburst ->
+ {undefined, L, ?ESOCK_OPT_SCTP_MAXBURST};
+ nodelay ->
+ {boolean, L, ?ESOCK_OPT_SCTP_NODELAY};
+ partial_delivery_point ->
+ {undefined, L, ?ESOCK_OPT_SCTP_PARTIAL_DELIVERY_POINT};
+ peer_addr_params ->
+ {undefined, L, ?ESOCK_OPT_SCTP_PEER_ADDR_PARAMS};
+ peer_auth_chunks ->
+ {undefined, L, ?ESOCK_OPT_SCTP_PEER_AUTH_CHUNKS};
+ primary_addr ->
+ {undefined, L, ?ESOCK_OPT_SCTP_PRIMARY_ADDR};
+ reset_streams ->
+ {undefined, L, ?ESOCK_OPT_SCTP_RESET_STREAMS};
+ rtoinfo -> {Key, L, ?ESOCK_OPT_SCTP_RTOINFO};
+ set_peer_primary_addr ->
+ {undefined, L, ?ESOCK_OPT_SCTP_SET_PEER_PRIMARY_ADDR};
+ status -> % ?ESOCK_OPT_SCTP_RTOINFO;
+ {undefined, L, ?ESOCK_OPT_SCTP_STATUS};
+ use_ext_recvinfo ->
+ {undefined, L, ?ESOCK_OPT_SCTP_USE_EXT_RECVINFO};
+ _ ->
+ not_supported(Level, Key)
+ end;
+enc_sockopt_type(Level, _Key) ->
+ not_supported(Level).
+
+%% Validate and possibly encode the setopt value
+%%
+enc_setopt_value(Level, Key, Value, Type) ->
+ %%
+ %% When guards are enough
+ %%
+ case Type of
+ boolean when is_boolean(Value) ->
+ Value;
+ integer when is_integer(Value) ->
+ Value;
+ non_neg_integer when is_integer(Value), 0 =< Value ->
+ Value;
+ byte when is_integer(Value), 0 =< Value, Value =< 255 ->
+ Value;
+ pid when is_pid(Value) ->
+ Value;
+ list when is_list(Value) ->
+ Value;
+ term ->
+ Value;
+ %%
+ mtu_discover
+ when Value =:= want;
+ Value =:= dont;
+ Value =:= do;
+ Value =:= probe;
+ is_integer(Value) ->
+ Value;
+ tos
+ when Value =:= lowdelay;
+ Value =:= throughput;
+ Value =:= reliability;
+ Value =:= mincost;
+ is_integer(Value) ->
+ Value;
+ addrform when Value =:= inet ->
+ enc_domain(Value);
+ _ ->
+ enc_setopt_value(Level, Key, {Type, Value})
+ end.
+%%
+enc_setopt_value(Level, Key, TypeValue) ->
+ %%
+ %% When matching is needed
+ %%
+ case TypeValue of
+ {rcvbuf, {N, BufSz} = BufSpec}
+ when is_integer(N), 0 < N, is_integer(BufSz), 0 < BufSz ->
+ %% N: Number of reads (when specifying length = 0)
+ %% BufSz: Size of the "read" buffer
+ BufSpec;
+ {rcvbuf, Value} ->
+ enc_setopt_value(Level, Key, Value, buf);
+ %%
+ {buf, default} ->
+ 0; % This will cause the nif-code to choose the default value
+ {buf, Value} ->
+ enc_setopt_value(Level, Key, Value, non_neg_integer);
+ %%
+ {linger, abort} ->
+ {true, 0};
+ {linger, {OnOff, Secs} = Linger}
+ when is_boolean(OnOff), is_integer(Secs), 0 =< Secs ->
+ Linger;
+ %%
+ {timeval,
+ #{sec := Sec, usec := USec} = Timeval}
+ when is_integer(Sec), is_integer(USec) ->
+ Timeval;
+ %%
+ {addr_if,
+ #{multiaddr := MA, interface := IF} = AddrIf}
+ when Level =:= ip
+ andalso ?IS_IPV4_ADDR(MA)
+ andalso (IF =:= any orelse ?IS_IPV4_ADDR(IF)) ->
+ AddrIf;
+ {addr_if,
+ #{multiaddr := MA, interface := IF} = AddrIf}
+ when Level =:= ipv6
+ andalso ?IS_IPV6_ADDR(MA)
+ andalso (is_integer(IF) andalso (0 =< IF)) ->
+ AddrIf;
+ %%
+ {addr_if_src,
+ #{multiaddr := MA, interface := IF, sourceaddr := SA} = AddrIfSrc}
+ when ?IS_IPV4_ADDR(MA)
+ andalso ?IS_IPV4_ADDR(IF)
+ andalso ?IS_IPV4_ADDR(SA) ->
+ AddrIfSrc;
+ %%
+ {msfilter, null = Value} ->
+ Value;
+ {addr_msfilter,
+ #{multiaddr := MA, interface := IF, fmode := FMode, slist := SL} =
+ AddrMsfilter}
+ when ?IS_IPV4_ADDR(MA)
+ andalso ?IS_IPV4_ADDR(IF)
+ andalso (FMode =:= include orelse FMode =:= exclude)
+ andalso is_list(SL) ->
+ AddrMsfilter;
+ {hops, default} ->
+ -1;
+ {hops, Value} ->
+ enc_setopt_value(Level, Key, Value, byte);
+ %%
+ {associnfo,
+ #{assoc_id := AssocId,
+ asocmaxrxt := MaxRxt,
+ num_peer_dests := NumPeerDests,
+ peer_rwnd := PeerRWND,
+ local_rwnd := LocalRWND,
+ cookie_life := CLife} = AssocInfo}
+ when is_integer(AssocId),
+ is_integer(MaxRxt), (MaxRxt >= 0),
+ is_integer(NumPeerDests), (NumPeerDests >= 0),
+ is_integer(PeerRWND), (PeerRWND >= 0),
+ is_integer(LocalRWND), (LocalRWND >= 0),
+ is_integer(CLife), (CLife >= 0) ->
+ AssocInfo;
+ {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} = Events}
+ when is_boolean(DataIn),
+ is_boolean(Assoc),
+ is_boolean(Addr),
+ is_boolean(SndFailure),
+ is_boolean(PeerError),
+ is_boolean(Shutdown),
+ is_boolean(PartialDelivery),
+ is_boolean(AdaptLayer),
+ is_boolean(Auth),
+ is_boolean(SndDry) ->
+ Events;
+ {initmsg,
+ #{num_outstreams := NumOut,
+ max_instreams := MaxIn,
+ max_attempts := MaxAttempts,
+ max_init_timeo := MaxInitTO} = InitMsg}
+ when is_integer(NumOut), (NumOut >= 0),
+ is_integer(MaxIn), (MaxIn >= 0),
+ is_integer(MaxAttempts), (MaxAttempts >= 0),
+ is_integer(MaxInitTO), (MaxInitTO >= 0) ->
+ InitMsg;
+ {rtoinfo,
+ #{assoc_id := AssocId,
+ initial := Init,
+ max := Max,
+ min := Min} = RTOInfo}
+ when is_integer(AssocId),
+ is_integer(Init), (Init >= 0),
+ is_integer(Max), (Max >= 0),
+ is_integer(Min), (Min >= 0) ->
+ RTOInfo;
+ {Type, Value} when Type =/= undefined ->
+ %% No match
+ not_supported(Level, Key, Value)
+ end.
+
+
+enc_shutdown_how(How) ->
+ case How of
+ read -> ?ESOCK_SHUTDOWN_HOW_READ;
+ write -> ?ESOCK_SHUTDOWN_HOW_WRITE;
+ read_write -> ?ESOCK_SHUTDOWN_HOW_READ_WRITE;
+ _ ->
+ invalid(how, How)
+ end.
+
+
+%% +++ Decode getopt value +++
+%%
+%% For the most part, we simply let the value pass through, but for some
+%% values we may need to do an actual decode.
+%%
+dec_getopt_value(Alg, tcp, congestion) ->
+ %% This string is NULL-terminated, but the general function we use
+ %% in the nif code does not know that. So, deal with it here.
+ {Str, _} = lists:splitwith(fun(0) -> false; (_) -> true end, Alg),
+ Str;
+dec_getopt_value(Value, _Level, _Key) ->
+ Value.
+
+%% ===========================================================================
+%% Error functions
+%%
+
+-dialyzer({no_return, not_supported/3}).
+not_supported(Level, Key, Value) ->
+ not_supported({Level, Key, Value}).
+%%
+-dialyzer({no_return, not_supported/2}).
+not_supported(Level, Key) ->
+ not_supported({Level, Key}).
+%%
+-dialyzer({no_return, not_supported/1}).
+not_supported(What) ->
+ invalid(not_supported, What).
+
+-dialyzer({no_return, invalid/2}).
+invalid(What, Info) ->
+ err({invalid, {What, Info}}).
+
+err(Reason) ->
+ erlang:error(Reason).
+
+%% ===========================================================================
+%% NIF functions
+%%
+
+nif_info() -> erlang:nif_error(undef).
+nif_info(_SRef) -> erlang:nif_error(undef).
+
+nif_command(_Command) -> erlang:nif_error(undef).
+
+nif_supports() -> erlang:nif_error(undef).
+nif_supports(_Key) -> erlang:nif_error(undef).
+nif_supports(_Key1, _Key2) -> erlang:nif_error(undef).
+
+nif_open(_FD, _Opts) -> erlang:nif_error(undef).
+nif_open(_Domain, _Type, _Protocol, _Opts) -> erlang:nif_error(undef).
+
+nif_bind(_SRef, _SockAddr) -> erlang:nif_error(undef).
+nif_bind(_SRef, _SockAddrs, _Action) -> erlang:nif_error(undef).
+
+nif_connect(_SRef) -> erlang:nif_error(undef).
+nif_connect(_SRef, _SockAddr) -> erlang:nif_error(undef).
+
+nif_listen(_SRef, _Backlog) -> erlang:nif_error(undef).
+
+nif_accept(_SRef, _Ref) -> erlang:nif_error(undef).
+
+nif_send(_SockRef, _SendRef, _Data, _Flags) -> erlang:nif_error(undef).
+nif_sendto(_SRef, _SendRef, _Data, _Dest, _Flags) -> erlang:nif_error(undef).
+nif_sendmsg(_SRef, _SendRef, _MsgHdr, _Flags) -> erlang:nif_error(undef).
+
+nif_recv(_SRef, _RecvRef, _Length, _Flags) -> erlang:nif_error(undef).
+nif_recvfrom(_SRef, _RecvRef, _Length, _Flags) -> erlang:nif_error(undef).
+nif_recvmsg(_SRef, _RecvRef, _BufSz, _CtrlSz, _Flags) -> erlang:nif_error(undef).
+
+nif_close(_SRef) -> erlang:nif_error(undef).
+nif_finalize_close(_SRef) -> erlang:nif_error(undef).
+nif_shutdown(_SRef, _How) -> erlang:nif_error(undef).
+
+nif_setopt(_Ref, _IsEnc, _Lev, _Key, _Val) -> erlang:nif_error(undef).
+nif_getopt(_Ref, _IsEnc, _Lev, _Key) -> erlang:nif_error(undef).
+
+nif_sockname(_Ref) -> erlang:nif_error(undef).
+nif_peername(_Ref) -> erlang:nif_error(undef).
+
+nif_cancel(_SRef, _Op, _Ref) -> erlang:nif_error(undef).
+
+%% ===========================================================================
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
deleted file mode 100644
index d05b4b818f..0000000000
--- a/erts/preloaded/src/socket.erl
+++ /dev/null
@@ -1,4192 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2018-2020. 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).
-
--compile(nowarn_nif_inline). %% Nobody please enable module inlining!
--compile(no_native).
--compile({no_auto_import,[error/1]}).
-
-%% Administrative and "global" utility functions
--export([
- on_load/0, on_load/1,
-
- number_of/0,
- which_sockets/0, which_sockets/1,
-
- ensure_sockaddr/1,
-
- debug/1, socket_debug/1,
- info/0, info/1,
- supports/0, supports/1, supports/2, supports/3
- ]).
-
--export([
- open/1, open/2, open/3, open/4,
- bind/2, bind/3,
- connect/2, connect/3,
- listen/1, listen/2,
- accept/1, accept/2,
-
- send/2, send/3, send/4,
- sendto/3, sendto/4, sendto/5,
- sendmsg/2, sendmsg/3, sendmsg/4,
-
- recv/1, recv/2, recv/3, recv/4,
- recvfrom/1, recvfrom/2, recvfrom/3, recvfrom/4,
- recvmsg/1, recvmsg/2, recvmsg/3, recvmsg/5,
-
- close/1,
- shutdown/2,
-
- setopt/4,
- getopt/3,
-
- sockname/1,
- peername/1,
-
- cancel/2
- ]).
-
--export_type([
- select_tag/0,
- select_ref/0,
- select_info/0,
-
- socket_counters/0,
- socket_counter/0,
- socket_info/0,
-
- domain/0,
- type/0,
- protocol/0,
- socket/0,
-
- port_number/0,
- ip_address/0,
- ip4_address/0,
- ip6_address/0,
- sockaddr/0,
- sockaddr_in4/0,
- sockaddr_in6/0,
- sockaddr_un/0,
- sockaddr_ll/0,
-
- send_flags/0,
- send_flag/0,
-
- recv_flags/0,
- recv_flag/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,
- raw_socket_option/0,
-
- timeval/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,
- ipv6_pktinfo/0,
- in6_flow_info/0,
- in6_scope_id/0,
- sctp_assoc_id/0,
- sctp_sndrcvinfo/0,
- sctp_event_subscribe/0,
- sctp_assocparams/0,
- sctp_initmsg/0,
- sctp_rtoinfo/0,
-
-
- msghdr_flag/0,
- msghdr_flags/0,
- msghdr/0,
- cmsghdr_level/0,
- cmsghdr_type/0,
- %% cmsghdr_data/0,
- cmsghdr_recv/0, cmsghdr_send/0,
-
- ee_origin/0,
- icmp_dest_unreach/0,
- icmpv6_dest_unreach/0,
- extended_err/0,
-
- uint8/0,
- uint16/0,
- uint20/0,
- uint32/0,
- int32/0
- ]).
-
-
--define(REGISTRY, socket_registry).
-
-
--type socket_counters() :: [{socket_counter(), non_neg_integer()}].
--type socket_counter() :: read_byte | read_fails | read_pkg | read_pkg_max |
- read_tries | read_waits |
- write_byte | write_fails | write_pkg | write_pkg_max |
- write_tries | write_waits |
- acc_success | acc_fails | acc_tries | acc_waits.
--type socket_info() :: #{domain := domain(),
- type := type(),
- protocol := protocol(),
- ctrl := pid(),
- ctype := normal | fromfd | {fromfd, integer()},
- counters := socket_counters(),
- num_readers := non_neg_integer(),
- num_writers := non_neg_integer(),
- num_acceptors := non_neg_integer(),
- writable := boolean(),
- readable := boolean()}.
-
--type uint8() :: 0..16#FF.
--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.
--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:
-%% 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.
-
--type ip_address() :: ip4_address() | ip6_address().
-
--type ip4_address() :: {0..255, 0..255, 0..255, 0..255}.
-
--type in6_flow_info() :: uint20().
--type in6_scope_id() :: uint32().
-
--type ip6_address() ::
- {0..65535,
- 0..65535,
- 0..65535,
- 0..65535,
- 0..65535,
- 0..65535,
- 0..65535,
- 0..65535}.
-
--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() :: lowdelay |
- throughput |
- reliability |
- mincost |
- integer().
-
-%% 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 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.
-
-%% 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()}.
-
--type ipv6_pmtudisc() :: ip_pmtudisc().
-
--type ipv6_pktinfo() :: #{
- addr := ip6_address(),
- ifindex := integer()
- }.
-
-
--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(),
- send_failure := boolean(),
- peer_error := boolean(),
- shutdown := boolean(),
- partial_delivery := boolean(),
- adaptation_layer := boolean(),
- authentication := boolean(),
- sender_dry := boolean()}.
-
--type sctp_assocparams() :: #{assoc_id := sctp_assoc_id(),
- 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 := sctp_assoc_id(),
- initial := uint32(),
- max := uint32(),
- min := uint32()}.
-
--type sockaddr_un() :: #{family := local,
- path := binary() | string()}.
--type sockaddr_in4() :: #{family := inet,
- port := port_number(),
- %% The 'broadcast' here is the "limited broadcast"
- addr := any | broadcast | 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_ll() :: #{family := packet,
- protocol := non_neg_integer(),
- ifindex := integer(),
- pkttype := packet_type(),
- hatype := non_neg_integer(),
- addr := binary()}.
--type packet_type() :: host | broadcast | multicast | otherhost |
- outgoing | loopback | user | kernel | fastroute |
- non_neg_integer().
--type sockaddr() :: sockaddr_in4() |
- sockaddr_in6() |
- sockaddr_un() |
- sockaddr_ll().
-
--define(OPEN2_OPTS_DEFAULTS, #{debug => false, dup => true}).
--define(OPEN4_OPTS_DEFAULTS, #{debug => false}).
-
--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 - This option is internal to our (OTP) implementation.
-%% socket - The socket layer (SOL_SOCKET).
-%% 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".
-%% Its up to the caller to make sure this is correct!
--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?
-%% Do we really need a sndbuf?
-
--type otp_socket_option() :: debug |
- iow |
- controlling_process |
- rcvbuf | % sndbuf |
- rcvctrlbuf |
- sndctrlbuf |
- meta |
- fd.
-%% 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 |
- domain |
- dontroute |
- error |
- keepalive |
- linger |
- mark |
- oobinline |
- passcred |
- peek_off |
- peercred |
- priority |
- protocol |
- rcvbuf |
- rcvbufforce |
- rcvlowat |
- rcvtimeo |
- reuseaddr |
- reuseport |
- rxq_ovfl |
- setfib |
- sndbuf |
- sndbufforce |
- sndlowat |
- sndtimeo |
- timestamp |
- type.
-
-%% Read-only options:
-%% mtu
-%%
-%% Options only valid on FreeBSD?:
-%% dontfrag
-%% Options only valid for RAW sockets:
-%% nodefrag (linux only?)
--type ip_socket_option() :: add_membership |
- add_source_membership |
- block_source |
- dontfrag |
- 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() ::
- addrform |
- add_membership |
- authhdr |
- auth_level |
- checksum |
- drop_membership |
- dstopts |
- esp_trans_level |
- esp_network_level |
- faith |
- flowinfo |
- hopopts |
- ipcomp_level |
- join_group |
- leave_group |
- mtu |
- mtu_discover |
- multicast_hops |
- multicast_if |
- multicast_loop |
- portrange |
- pktoptions |
- recverr |
- recvhoplimit | hoplimit |
- recvpktinfo | pktinfo |
- recvtclass |
- router_alert |
- rthdr |
- tclass |
- unicast_hops |
- use_min_mtu |
- v6only.
-
--type tcp_socket_option() :: congestion |
- cork |
- info |
- keepcnt |
- keepidle |
- keepintvl |
- maxseg |
- md5sig |
- nodelay |
- noopt |
- nopush |
- syncnt |
- user_timeout.
-
--type udp_socket_option() :: cork.
-
--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 raw_socket_option() :: filter.
-
-%% -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() |
-%% raw_socket_option() |
-%% plain_socket_option().
-
--record(socket, {ref :: reference()}).
-
--opaque socket() :: #socket{}.
-
--type send_flags() :: [send_flag()].
--type send_flag() :: confirm |
- dontroute |
- eor |
- more |
- nosignal |
- oob.
-
-%% Note that not all of these flags are useful for every recv function!
-%%
--type recv_flags() :: [recv_flag()].
--type recv_flag() :: cmsg_cloexec |
- errqueue |
- oob |
- peek |
- trunc.
-
--type shutdown_how() :: read | write | read_write.
-
--type msghdr_flag() :: ctrunc | eor | errqueue | oob | trunc.
--type msghdr_flags() :: [msghdr_flag()].
--type msghdr() :: #{
- %% *Optional* target address
- %% 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_recv()] | [cmsghdr_send()],
-
- %% Only valid with recvmsg
- 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 cmsghdr_level() :: socket | ip | ipv6 | integer().
--type cmsghdr_type() :: credentials |
- hoplevel |
- origdstaddr |
- pktinfo |
- recvtos |
- rights |
- timestamp |
- tos |
- ttl |
- integer().
--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 := recvtos, data := ip_tos()} |
- #{level := ip, type := ttl, data := integer()} |
- #{level := ip, type := recvttl, data := integer()} |
- #{level := ip, type := pktinfo, data := ip_pktinfo()} |
- #{level := ip, type := origdstaddr, data := sockaddr_in4()} |
- #{level := ip, type := recverr, data := extended_err() | binary()} |
- #{level := ip, type := integer(), data := binary()} |
- #{level := ipv6, type := hoplevel, data := integer()} |
- #{level := ipv6, type := pktinfo, data := ipv6_pktinfo()} |
- #{level := ipv6, type := recverr, data := extended_err() | binary()} |
- #{level := ipv6, type := tclass, data := integer()} |
- #{level := ipv6, type := integer(), data := binary()} |
- #{level := integer(), type := integer(), data := binary()}.
--type cmsghdr_send() ::
- #{level := socket, type := timestamp, data := binary()} |
- #{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() | binary()} |
- #{level := ip, type := ttl, data := integer() | binary()} |
- #{level := ip, type := integer(), data := binary()} |
- #{level := ipv6, type := tclass, data := integer()} |
- #{level := ipv6, type := integer(), data := binary()} |
- #{level := udp, type := integer(), data := binary()} |
- #{level := integer(), type := integer(), data := binary()}.
-
--type ee_origin() :: none | local | icmp | icmp6 | uint8().
--type icmp_dest_unreach() :: net_unreach | host_unreach | port_unreach | frag_needed |
- net_unknown | host_unknown | uint8().
--type icmpv6_dest_unreach() :: noroute | adm_prohibited | not_neighbour | addr_unreach |
- port_unreach | policy_fail | reject_route | uint8().
--type extended_err() ::
- #{error := term(),
- origin := icmp,
- type := dest_unreach,
- code := icmp_dest_unreach(),
- info := uint32(),
- data := uint32(),
- offender := undefined | sockaddr()} |
- #{error := term(),
- origin := icmp,
- type := time_exceeded | uint8(),
- code := uint8(),
- info := uint32(),
- data := uint32(),
- offender := undefined | sockaddr()} |
- #{error := term(),
- origin := icmp6,
- type := dest_unreach,
- code := icmpv6_dest_unreach(),
- info := uint32(),
- data := uint32(),
- offender := undefined | sockaddr()} |
- #{error := term(),
- origin := icmp6,
- type := pkt_toobig | time_exceeded | uint8(),
- code := uint8(),
- info := uint32(),
- data := uint32(),
- offender := undefined | sockaddr()} |
- #{error := term(),
- origin := ee_origin(),
- type := uint8(),
- code := uint8(),
- info := uint32(),
- data := uint32(),
- offender := undefined | sockaddr()}.
-
--opaque select_tag() :: atom().
--opaque select_ref() :: reference().
-
-%% -record(select_info, {tag :: select_tag(), ref :: select_ref()}).
-
--type select_info() :: {select_info, select_tag(), select_ref()}.
-
--define(SELECT_INFO(T, R), {select_info, T, R}).
--define(SELECT(T, R), {select, ?SELECT_INFO(T, R)}).
-
-
-%% This is used in messages sent from the nif-code to erlang processes:
-%%
-%% {?ESOCK_TAG, Socket :: socket(), Tag :: atom(), Info :: term()}
-%%
--define(ESOCK_TAG, '$socket').
-
--define(ESOCK_DOMAIN_LOCAL, 1).
--define(ESOCK_DOMAIN_UNIX, ?ESOCK_DOMAIN_LOCAL).
--define(ESOCK_DOMAIN_INET, 2).
--define(ESOCK_DOMAIN_INET6, 3).
-
--define(ESOCK_TYPE_STREAM, 1).
--define(ESOCK_TYPE_DGRAM, 2).
--define(ESOCK_TYPE_RAW, 3).
-%% -define(ESOCK_TYPE_RDM, 4).
--define(ESOCK_TYPE_SEQPACKET, 5).
-
--define(ESOCK_PROTOCOL_DEFAULT, 0).
--define(ESOCK_PROTOCOL_IP, 1).
--define(ESOCK_PROTOCOL_TCP, 2).
--define(ESOCK_PROTOCOL_UDP, 3).
--define(ESOCK_PROTOCOL_SCTP, 4).
--define(ESOCK_PROTOCOL_ICMP, 5).
--define(ESOCK_PROTOCOL_IGMP, 6).
-
--define(ESOCK_LISTEN_BACKLOG_DEFAULT, 5).
-
--define(ESOCK_ACCEPT_TIMEOUT_DEFAULT, infinity).
-
--define(ESOCK_SEND_FLAG_CONFIRM, 0).
--define(ESOCK_SEND_FLAG_DONTROUTE, 1).
--define(ESOCK_SEND_FLAG_EOR, 2).
--define(ESOCK_SEND_FLAG_MORE, 3).
--define(ESOCK_SEND_FLAG_NOSIGNAL, 4).
--define(ESOCK_SEND_FLAG_OOB, 5).
-
--define(ESOCK_SEND_FLAGS_DEFAULT, []).
--define(ESOCK_SEND_TIMEOUT_DEFAULT, infinity).
--define(ESOCK_SENDTO_FLAGS_DEFAULT, []).
--define(ESOCK_SENDTO_TIMEOUT_DEFAULT, ?ESOCK_SEND_TIMEOUT_DEFAULT).
--define(ESOCK_SENDMSG_FLAGS_DEFAULT, []).
--define(ESOCK_SENDMSG_TIMEOUT_DEFAULT, ?ESOCK_SEND_TIMEOUT_DEFAULT).
-
--define(ESOCK_RECV_FLAG_CMSG_CLOEXEC, 0).
--define(ESOCK_RECV_FLAG_ERRQUEUE, 1).
--define(ESOCK_RECV_FLAG_OOB, 2).
--define(ESOCK_RECV_FLAG_PEEK, 3).
--define(ESOCK_RECV_FLAG_TRUNC, 4).
-
--define(ESOCK_RECV_FLAGS_DEFAULT, []).
--define(ESOCK_RECV_TIMEOUT_DEFAULT, infinity).
-
--define(ESOCK_OPT_LEVEL_OTP, 0).
--define(ESOCK_OPT_LEVEL_SOCKET, 1).
--define(ESOCK_OPT_LEVEL_IP, 2).
--define(ESOCK_OPT_LEVEL_IPV6, 3).
--define(ESOCK_OPT_LEVEL_TCP, 4).
--define(ESOCK_OPT_LEVEL_UDP, 5).
--define(ESOCK_OPT_LEVEL_SCTP, 6).
-
-%% *** OTP (socket) options
--define(ESOCK_OPT_OTP_DEBUG, 1).
--define(ESOCK_OPT_OTP_IOW, 2).
--define(ESOCK_OPT_OTP_CTRL_PROC, 3).
--define(ESOCK_OPT_OTP_RCVBUF, 4).
-%%-define(ESOCK_OPT_OTP_SNDBUF, 5).
--define(ESOCK_OPT_OTP_RCVCTRLBUF, 6).
--define(ESOCK_OPT_OTP_SNDCTRLBUF, 7).
--define(ESOCK_OPT_OTP_FD, 8).
--define(ESOCK_OPT_OTP_META, 9).
--define(ESOCK_OPT_OTP_DOMAIN, 16#FF01). % INTERNAL
--define(ESOCK_OPT_OTP_TYPE, 16#FF02). % INTERNAL
--define(ESOCK_OPT_OTP_PROTOCOL, 16#FF03). % INTERNAL
--define(ESOCK_OPT_OTP_DTP, 16#FF04). % INTERNAL
-
-%% *** SOCKET (socket) options
--define(ESOCK_OPT_SOCK_ACCEPTCONN, 1).
-%% -define(ESOCK_OPT_SOCK_ACCEPTFILTER, 2). % FreeBSD
--define(ESOCK_OPT_SOCK_BINDTODEVICE, 3).
--define(ESOCK_OPT_SOCK_BROADCAST, 4).
-%% -define(ESOCK_OPT_SOCK_BUSY_POLL, 5).
--define(ESOCK_OPT_SOCK_DEBUG, 6).
--define(ESOCK_OPT_SOCK_DOMAIN, 7).
--define(ESOCK_OPT_SOCK_DONTROUTE, 8).
-%% -define(ESOCK_OPT_SOCK_ERROR, 9).
--define(ESOCK_OPT_SOCK_KEEPALIVE, 10).
--define(ESOCK_OPT_SOCK_LINGER, 11).
-%% -define(ESOCK_OPT_SOCK_MARK, 12).
--define(ESOCK_OPT_SOCK_OOBINLINE, 13).
--define(ESOCK_OPT_SOCK_PASSCRED, 14).
--define(ESOCK_OPT_SOCK_PEEK_OFF, 15).
-%% -define(ESOCK_OPT_SOCK_PEERCRED, 16).
--define(ESOCK_OPT_SOCK_PRIORITY, 17).
--define(ESOCK_OPT_SOCK_PROTOCOL, 18).
--define(ESOCK_OPT_SOCK_RCVBUF, 19).
-%% -define(ESOCK_OPT_SOCK_RCVBUFFORCE, 20).
--define(ESOCK_OPT_SOCK_RCVLOWAT, 21).
--define(ESOCK_OPT_SOCK_RCVTIMEO, 22).
--define(ESOCK_OPT_SOCK_REUSEADDR, 23).
--define(ESOCK_OPT_SOCK_REUSEPORT, 24).
-%% -define(ESOCK_OPT_SOCK_RXQ_OVFL, 25).
-%% -define(ESOCK_OPT_SOCK_SETFIB, 26). % FreeBSD
--define(ESOCK_OPT_SOCK_SNDBUF, 27).
-%% -define(ESOCK_OPT_SOCK_SNDBUFFORCE, 28).
--define(ESOCK_OPT_SOCK_SNDLOWAT, 29).
--define(ESOCK_OPT_SOCK_SNDTIMEO, 30).
--define(ESOCK_OPT_SOCK_TIMESTAMP, 31).
--define(ESOCK_OPT_SOCK_TYPE, 32).
-
-%% *** IP (socket) options
--define(ESOCK_OPT_IP_ADD_MEMBERSHIP, 1).
--define(ESOCK_OPT_IP_ADD_SOURCE_MEMBERSHIP, 2).
--define(ESOCK_OPT_IP_BLOCK_SOURCE, 3).
-%% -define(ESOCK_OPT_IP_DONTFRAG, 4). % FreeBSD
--define(ESOCK_OPT_IP_DROP_MEMBERSHIP, 5).
--define(ESOCK_OPT_IP_DROP_SOURCE_MEMBERSHIP, 6).
--define(ESOCK_OPT_IP_FREEBIND, 7).
--define(ESOCK_OPT_IP_HDRINCL, 8).
--define(ESOCK_OPT_IP_MINTTL, 9).
--define(ESOCK_OPT_IP_MSFILTER, 10).
--define(ESOCK_OPT_IP_MTU, 11).
--define(ESOCK_OPT_IP_MTU_DISCOVER, 12).
--define(ESOCK_OPT_IP_MULTICAST_ALL, 13).
--define(ESOCK_OPT_IP_MULTICAST_IF, 14).
--define(ESOCK_OPT_IP_MULTICAST_LOOP, 15).
--define(ESOCK_OPT_IP_MULTICAST_TTL, 16).
--define(ESOCK_OPT_IP_NODEFRAG, 17).
-%% -define(ESOCK_OPT_IP_OPTIONS, 18). % FreeBSD
--define(ESOCK_OPT_IP_PKTINFO, 19).
--define(ESOCK_OPT_IP_RECVDSTADDR, 20). % FreeBSD
--define(ESOCK_OPT_IP_RECVERR, 21).
--define(ESOCK_OPT_IP_RECVIF, 22).
--define(ESOCK_OPT_IP_RECVOPTS, 23).
--define(ESOCK_OPT_IP_RECVORIGDSTADDR, 24).
--define(ESOCK_OPT_IP_RECVTOS, 25).
--define(ESOCK_OPT_IP_RECVTTL, 26).
--define(ESOCK_OPT_IP_RETOPTS, 27).
--define(ESOCK_OPT_IP_ROUTER_ALERT, 28).
--define(ESOCK_OPT_IP_SENDSRCADDR, 29). % FreeBSD
--define(ESOCK_OPT_IP_TOS, 30).
--define(ESOCK_OPT_IP_TRANSPARENT, 31).
--define(ESOCK_OPT_IP_TTL, 32).
--define(ESOCK_OPT_IP_UNBLOCK_SOURCE, 33).
-
-%% *** IPv6 (socket) options
--define(ESOCK_OPT_IPV6_ADDRFORM, 1).
--define(ESOCK_OPT_IPV6_ADD_MEMBERSHIP, 2).
--define(ESOCK_OPT_IPV6_AUTHHDR, 3). % Obsolete?
-%% -define(ESOCK_OPT_IPV6_AUTH_LEVEL, 4). % FreeBSD
-%% -define(ESOCK_OPT_IPV6_CHECKSUM, 5). % FreeBSD
--define(ESOCK_OPT_IPV6_DROP_MEMBERSHIP, 6).
--define(ESOCK_OPT_IPV6_DSTOPTS, 7).
-%% -define(ESOCK_OPT_IPV6_ESP_NETWORK_LEVEL, 8). % FreeBSD
-%% -define(ESOCK_OPT_IPV6_ESP_TRANS_LEVEL, 9). % FreeBSD
-%% -define(ESOCK_OPT_IPV6_FAITH, 10). % FreeBSD
--define(ESOCK_OPT_IPV6_FLOWINFO, 11).
--define(ESOCK_OPT_IPV6_HOPLIMIT, 12).
--define(ESOCK_OPT_IPV6_HOPOPTS, 13).
-%% -define(ESOCK_OPT_IPV6_IPCOMP_LEVEL, 14). % FreeBSD
-%% -define(ESOCK_OPT_IPV6_JOIN_GROUP, 15). % FreeBSD
-%% -define(ESOCK_OPT_IPV6_LEAVE_GROUP, 16). % FreeBSD
--define(ESOCK_OPT_IPV6_MTU, 17).
--define(ESOCK_OPT_IPV6_MTU_DISCOVER, 18).
--define(ESOCK_OPT_IPV6_MULTICAST_HOPS, 19).
--define(ESOCK_OPT_IPV6_MULTICAST_IF, 20).
--define(ESOCK_OPT_IPV6_MULTICAST_LOOP, 21).
-%% -define(ESOCK_OPT_IPV6_PORTRANGE, 22). % FreeBSD
-%% -define(ESOCK_OPT_IPV6_PKTOPTIONS, 23). % FreeBSD
--define(ESOCK_OPT_IPV6_RECVERR, 24).
--define(ESOCK_OPT_IPV6_RECVHOPLIMIT, 25).
--define(ESOCK_OPT_IPV6_RECVPKTINFO, 26). % On FreeBSD: PKTINFO
--define(ESOCK_OPT_IPV6_RECVTCLASS, 27).
--define(ESOCK_OPT_IPV6_ROUTER_ALERT, 28).
--define(ESOCK_OPT_IPV6_RTHDR, 29).
--define(ESOCK_OPT_IPV6_TCLASS, 30). % FreeBSD
--define(ESOCK_OPT_IPV6_UNICAST_HOPS, 31).
-%% -define(ESOCK_OPT_IPV6_USE_MIN_MTU, 32). % FreeBSD
--define(ESOCK_OPT_IPV6_V6ONLY, 33).
-
-%% *** TCP (socket) options
--define(ESOCK_OPT_TCP_CONGESTION, 1).
--define(ESOCK_OPT_TCP_CORK, 2).
-%% -define(ESOCK_OPT_TCP_INFO, 3).
-%% -define(ESOCK_OPT_TCP_KEEPCNT, 4).
-%% -define(ESOCK_OPT_TCP_KEEPIDLE, 5).
-%% -define(ESOCK_OPT_TCP_KEEPINTVL, 6).
--define(ESOCK_OPT_TCP_MAXSEG, 7).
-%% -define(ESOCK_OPT_TCP_MD5SIG, 8).
--define(ESOCK_OPT_TCP_NODELAY, 9).
-%% -define(ESOCK_OPT_TCP_NOOPT, 10).
-%% -define(ESOCK_OPT_TCP_NOPUSH, 11).
-%% -define(ESOCK_OPT_TCP_SYNCNT, 12).
-%% -define(ESOCK_OPT_TCP_USER_TIMEOUT, 13).
-
-%% *** UDP (socket) options
--define(ESOCK_OPT_UDP_CORK, 1).
-
-%% *** SCTP (socket) options
-%% -define(ESOCK_OPT_SCTP_ADAPTION_LAYER, 1).
--define(ESOCK_OPT_SCTP_ASSOCINFO, 2).
-%% -define(ESOCK_OPT_SCTP_AUTH_ACTIVE_KEY, 3).
-%% -define(ESOCK_OPT_SCTP_AUTH_ASCONF, 4).
-%% -define(ESOCK_OPT_SCTP_AUTH_CHUNK, 5).
-%% -define(ESOCK_OPT_SCTP_AUTH_KEY, 6).
-%% -define(ESOCK_OPT_SCTP_AUTH_DELETE_KEY, 7).
--define(ESOCK_OPT_SCTP_AUTOCLOSE, 8).
-%% -define(ESOCK_OPT_SCTP_CONTEXT, 9).
-%% -define(ESOCK_OPT_SCTP_DEFAULT_SEND_PARAMS, 10).
-%% -define(ESOCK_OPT_SCTP_DELAYED_ACK_TIME, 11).
--define(ESOCK_OPT_SCTP_DISABLE_FRAGMENTS, 12).
-%% -define(ESOCK_OPT_SCTP_HMAC_IDENT, 13).
--define(ESOCK_OPT_SCTP_EVENTS, 14).
-%% -define(ESOCK_OPT_SCTP_EXPLICIT_EOR, 15).
-%% -define(ESOCK_OPT_SCTP_FRAGMENT_INTERLEAVE, 16).
-%% -define(ESOCK_OPT_SCTP_GET_PEER_ADDR_INFO, 17).
--define(ESOCK_OPT_SCTP_INITMSG, 18).
-%% -define(ESOCK_OPT_SCTP_I_WANT_MAPPED_V4_ADDR, 19).
-%% -define(ESOCK_OPT_SCTP_LOCAL_AUTH_CHUNKS, 20).
--define(ESOCK_OPT_SCTP_MAXSEG, 21).
-%% -define(ESOCK_OPT_SCTP_MAXBURST, 22).
--define(ESOCK_OPT_SCTP_NODELAY, 23).
-%% -define(ESOCK_OPT_SCTP_PARTIAL_DELIVERY_POINT, 24).
-%% -define(ESOCK_OPT_SCTP_PEER_ADDR_PARAMS, 25).
-%% -define(ESOCK_OPT_SCTP_PEER_AUTH_CHUNKS, 26).
-%% -define(ESOCK_OPT_SCTP_PRIMARY_ADDR, 27).
-%% -define(ESOCK_OPT_SCTP_RESET_STREAMS, 28).
--define(ESOCK_OPT_SCTP_RTOINFO, 29).
-%% -define(ESOCK_OPT_SCTP_SET_PEER_PRIMARY_ADDR, 30).
-%% -define(ESOCK_OPT_SCTP_STATUS, 31).
-%% -define(ESOCK_OPT_SCTP_USE_EXT_RECVINFO, 32).
-
--define(ESOCK_SHUTDOWN_HOW_READ, 0).
--define(ESOCK_SHUTDOWN_HOW_WRITE, 1).
--define(ESOCK_SHUTDOWN_HOW_READ_WRITE, 2).
-
-
--define(ESOCK_SUPPORTS_OPTIONS, 16#0001).
--define(ESOCK_SUPPORTS_SCTP, 16#0002).
--define(ESOCK_SUPPORTS_IPV6, 16#0003).
--define(ESOCK_SUPPORTS_LOCAL, 16#0004).
--define(ESOCK_SUPPORTS_SEND_FLAGS, 16#0005).
--define(ESOCK_SUPPORTS_RECV_FLAGS, 16#0006).
--define(ESOCK_SUPPORTS_NETNS, 16#0007).
-
-
-%% ===========================================================================
-%%
-%% 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 :: map().
-
-on_load(Extra) ->
- %% This is spawned as a system process to prevent init:restart/0 from
- %% killing it.
- Pid = erts_internal:spawn_system_process(?REGISTRY, start, []),
- DebugFilename =
- case os:get_env_var("ESOCK_DEBUG_FILENAME") of
- "*" ->
- "/tmp/esock-dbg-??????";
- F ->
- F
- end,
- ok =
- erlang:load_nif(
- atom_to_list(?MODULE),
- case DebugFilename of
- false ->
- Extra#{registry => Pid};
- _ ->
- Extra
- #{registry => Pid,
- debug => true,
- socket_debug => true,
- debug_filename =>
- enc_path(DebugFilename)}
- end).
-
-%% *** number_of ***
-%%
-%% Interface function to the socket registry
-%% returns the number of existing (and "alive") sockets.
-%%
--spec number_of() -> non_neg_integer().
-
-number_of() ->
- ?REGISTRY:number_of().
-
-
-%% *** which_sockets/0,1 ***
-%%
-%% Interface function to the socket registry
-%% Returns a list of all the sockets, accoring to the filter rule.
-%%
--spec which_sockets() -> [socket()].
-
-which_sockets() ->
- ?REGISTRY:which_sockets(fun(_) -> true end).
-
--spec which_sockets(FilterRule) -> [socket()] when
- FilterRule :: inet | inet6 |
- stream | dgram | seqpacket |
- sctp | tcp | udp |
- pid() |
- fun((socket_info()) -> boolean()).
-
-which_sockets(Domain)
- when ((Domain =:= inet) orelse (Domain =:= inet6)) ->
- ?REGISTRY:which_sockets(fun(#{domain := D}) when (D =:= Domain) -> true;
- (_) -> false end);
-which_sockets(Type)
- when ((Type =:= stream) orelse (Type =:= dgram) orelse (Type =:= seqpacket)) ->
- ?REGISTRY:which_sockets(fun(#{type := T}) when (T =:= Type) -> true;
- (_) -> false end);
-which_sockets(Proto)
- when ((Proto =:= sctp) orelse (Proto =:= tcp) orelse (Proto =:= udp)) ->
- ?REGISTRY:which_sockets(fun(#{protocol := P}) when (P =:= Proto) -> true;
- (_) -> false end);
-which_sockets(CTRL)
- when is_pid(CTRL) ->
- ?REGISTRY:which_sockets(fun(#{ctrl := C}) when (C =:= CTRL) -> true;
- (_) -> false end);
-which_sockets(Filter) when is_function(Filter, 1) ->
- ?REGISTRY:which_sockets(Filter).
-
-
-
-
-
--spec info() -> map().
-
-info() ->
- nif_info().
-
-
--spec debug(D :: boolean()) -> ok.
-debug(D) when is_boolean(D) ->
- nif_command(#{command => ?FUNCTION_NAME,
- data => D}).
-
--spec socket_debug(D :: boolean()) -> ok.
-socket_debug(D) when is_boolean(D) ->
- nif_command(#{command => ?FUNCTION_NAME,
- data => D}).
-
-
-%% ===========================================================================
-%%
-%% info - Get miscellaneous information about a socket.
-%%
-%% Generates a list of various info about the socket, such as counter values.
-%%
-%% Do *not* call this function often.
-%%
-%% ===========================================================================
-
--spec info(Socket) -> socket_info() when
- Socket :: socket().
-
-info(#socket{ref = SockRef}) ->
- nif_info(SockRef).
-
-
-
-%% ===========================================================================
-%%
-%% 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()}].
--type supports_send_flags() :: [{send_flag(), boolean()}].
--type supports_recv_flags() :: [{recv_flag(), boolean()}].
-
--spec supports() -> [{options, supports_options()} |
- {sctp, boolean()} |
- {ipv6, boolean()} |
- {local, boolean()} |
- {send_flags, supports_send_flags()} |
- {recv_flags, supports_recv_flags()} |
- {netns, boolean()}].
-
-supports() ->
- [
- {options, supports(options)},
- {sctp, supports(sctp)},
- {ipv6, supports(ipv6)},
- {local, supports(local)},
- {send_flags, supports(send_flags)},
- {recv_flags, supports(recv_flags)},
- {netns, supports(netns)}
- ].
-
-
--dialyzer({no_contracts, supports/1}).
--spec supports(options) -> supports_options();
- (sctp) -> boolean();
- (ipv6) -> boolean();
- (local) -> boolean();
- (send_flags) -> supports_send_flags();
- (recv_flags) -> supports_recv_flags();
- (netns) -> boolean();
- (Key1) -> false when
- Key1 :: term().
-
-supports(options) ->
- nif_supports(?ESOCK_SUPPORTS_OPTIONS);
-supports(sctp) ->
- nif_supports(?ESOCK_SUPPORTS_SCTP);
-supports(ipv6) ->
- nif_supports(?ESOCK_SUPPORTS_IPV6);
-supports(local) ->
- nif_supports(?ESOCK_SUPPORTS_LOCAL);
-supports(send_flags) ->
- nif_supports(?ESOCK_SUPPORTS_SEND_FLAGS);
-supports(recv_flags) ->
- nif_supports(?ESOCK_SUPPORTS_RECV_FLAGS);
-supports(netns) ->
- nif_supports(?ESOCK_SUPPORTS_NETNS);
-supports(_Key1) ->
- false.
-
--dialyzer({no_contracts, supports/2}).
--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();
- (send_flags, SendFlag :: send_flag()) -> boolean();
- (recv_flags, RecvFlag :: recv_flag()) -> boolean();
- (Key1, Key2) -> false when
- Key1 :: term(),
- Key2 :: term().
-
-supports(options, Level) ->
- proplists:get_value(Level, supports(options), false);
-supports(send_flags, Flag) ->
- proplists:get_value(Flag, supports(send_flags), false);
-supports(recv_flags, Flag) ->
- proplists:get_value(Flag, supports(recv_flags), false);
-supports(_Key1, _Level) ->
- false.
-
-
--spec supports(options, socket, Opt :: socket_option()) -> boolean();
- (options, ip, Opt :: ip_socket_option()) -> boolean();
- (options, ipv6, Opt :: ipv6_socket_option()) -> boolean();
- (options, tcp, Opt :: tcp_socket_option()) -> boolean();
- (options, udp, Opt :: udp_socket_option()) -> boolean();
- (options, sctp, Opt :: sctp_socket_option()) -> boolean();
- (Key1, Key2, Key3) -> false when
- Key1 :: term(),
- Key2 :: term(),
- Key3 :: term().
-
--dialyzer({no_contracts, supports/3}).
-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.
-
-
-
-%% ===========================================================================
-%%
-%% The proper socket API
-%%
-%% ===========================================================================
-
-%% ===========================================================================
-%%
-%% <KOLLA>
-%%
-%% 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 may therefor need monitor function(s):
-%%
-%% socket:monitor(Socket)
-%% socket:demonitor(Socket)
-%%
-%% </KOLLA>
-%%
-
-
-
-%% ===========================================================================
-%%
-%% open - create an endpoint for communication
-%%
-%% Extra: Currently only used for netns
-%%
-
--spec open(FD) -> {ok, Socket} | {error, Reason} when
- FD :: integer(),
- Socket :: socket(),
- Reason :: term().
-
-open(FD) ->
- open(FD, ?OPEN2_OPTS_DEFAULTS).
-
--spec open(FD, Opts) -> {ok, Socket} | {error, Reason} when
- FD :: integer(),
- Opts :: map(),
- Socket :: socket(),
- Reason :: term();
- (Domain, Type) -> {ok, Socket} | {error, Reason} when
- Domain :: domain(),
- Type :: type(),
- Socket :: socket(),
- Reason :: term().
-
-open(FD, Opts) when is_integer(FD) andalso is_map(Opts) ->
- case nif_open(FD, ensure_open2_opts(Opts)) of
- {ok, SockRef} ->
- Socket = #socket{ref = SockRef},
- {ok, Socket};
- {error, _} = ERROR ->
- ERROR
- end;
-open(Domain, Type) ->
- open(Domain, Type, default).
-
--spec open(Domain, Type, Protocol) -> {ok, Socket} | {error, Reason} when
- Domain :: domain(),
- Type :: type(),
- Protocol :: default | protocol(),
- Socket :: socket(),
- Reason :: term().
-
-open(Domain, Type, Protocol) ->
- open(Domain, Type, Protocol, ?OPEN4_OPTS_DEFAULTS).
-
--spec open(Domain, Type, Protocol, Opts) -> {ok, Socket} | {error, Reason} when
- Domain :: domain(),
- Type :: type(),
- Protocol :: default | protocol(),
- Opts :: map(),
- Socket :: socket(),
- Reason :: term().
-
-open(Domain, Type, Protocol, Opts) when is_map(Opts) ->
- try
- begin
- EDomain = enc_domain(Domain),
- EType = enc_type(Type),
- EProtocol = enc_protocol(Protocol),
- Opts_1 =
- case Opts of
- #{netns := Path} when is_list(Path) ->
- Opts#{netns := enc_path(Path)};
- _ ->
- Opts
- end,
- nif_open(EDomain, EType, EProtocol, Opts_1)
- end
- of
- {ok, SockRef} ->
- Socket = #socket{ref = SockRef},
- {ok, Socket};
- {error, _} = ERROR ->
- ERROR
- catch
- throw:ERROR ->
- ERROR
- end.
-
-
-
-%% ===========================================================================
-%%
-%% bind - bind a name to a socket
-%%
-%% Note that Addr can only have the value of broadcast *if* Domain =:= inet!
-%%
-
--spec bind(Socket, Addr) -> {ok, Port} | {error, Reason} when
- Socket :: socket(),
- Addr :: any | broadcast | loopback | sockaddr(),
- Port :: port_number(),
- Reason :: term().
-
-bind(#socket{ref = SockRef}, Addr)
- when ((Addr =:= any) orelse
- (Addr =:= broadcast) orelse
- (Addr =:= loopback)) ->
- try
- case which_domain(SockRef) of
- inet ->
- nif_bind(SockRef, ?SOCKADDR_IN4_DEFAULT(Addr));
- inet6 when (Addr =:= any) orelse (Addr =:= loopback) ->
- nif_bind(SockRef, ?SOCKADDR_IN6_DEFAULT(Addr));
- Domain ->
- invalid_domain(Domain)
- end
- catch
- throw:ERROR ->
- ERROR
- end;
-bind(#socket{ref = SockRef} = _Socket, Addr) when is_map(Addr) ->
- try
- nif_bind(SockRef, ensure_sockaddr(Addr))
- catch
- throw:ERROR ->
- ERROR
- end.
-
-
-
-%% ===========================================================================
-%%
-%% 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}, Addrs, Action)
- when is_list(Addrs) andalso ((Action =:= add) orelse (Action =:= remove)) ->
- try
- begin
- {Domain, Type, Proto} = which_dtp(SockRef),
- ensure_domain(Domain, [inet, inet6]),
- ensure_type(Type, seqpacket),
- ensure_protocol(Proto, sctp),
- validate_addrs(Domain, Addrs),
- nif_bind(SockRef, Addrs, Action)
- end
- catch
- throw:ERROR ->
- ERROR
- end.
-
-ensure_domain(Domain, [Domain | _]) -> ok;
-ensure_domain(Domain, [_ | Domains]) -> ensure_domain(Domain, Domains);
-ensure_domain(Domain, []) -> invalid_domain(Domain).
-
-ensure_type(Type, Type) -> ok;
-ensure_type(Type, _) -> invalid_type(Type).
-
-ensure_protocol(Proto, Proto) -> ok;
-ensure_protocol(Proto, _) -> invalid_protocol(Proto).
-
-
-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
-%%
-
--spec connect(Socket, SockAddr) -> ok | {error, Reason} when
- Socket :: socket(),
- SockAddr :: sockaddr(),
- Reason :: term().
-
-connect(Socket, SockAddr) ->
- connect(Socket, SockAddr, infinity).
-
--spec connect(Socket, SockAddr, nowait) ->
- ok | {select, SelectInfo} | {error, Reason} when
- Socket :: socket(),
- SockAddr :: sockaddr(),
- SelectInfo :: select_info(),
- Reason :: term()
- ; (Socket, SockAddr, Timeout) -> ok | {error, Reason} when
- Socket :: socket(),
- SockAddr :: sockaddr(),
- Timeout :: timeout(),
- Reason :: term().
-
-%% <KOLLA>
-%% Is it possible to connect with family = local for the (dest) sockaddr?
-%% </KOLLA>
-connect(#socket{ref = SockRef}, SockAddr, Timeout) ->
- try
- do_connect(SockRef, ensure_sockaddr(SockAddr), deadline(Timeout))
- catch
- throw:ERROR ->
- ERROR
- end.
-
-
-do_connect(SockRef, SockAddr, Deadline) ->
- case nif_connect(SockRef, ensure_sockaddr(SockAddr)) of
-
- ok ->
- %% Connected!
- ok;
-
-
- {ok, Ref} when (Deadline =:= nowait) ->
- %% Connecting, but the caller does not want to wait...
- ?SELECT(connect, Ref);
-
- {ok, Ref} ->
- %% Connecting...
- Timeout = timeout(Deadline),
- receive
- {?ESOCK_TAG, _Socket, select, Ref} ->
- nif_finalize_connection(SockRef);
-
- {?ESOCK_TAG, _Socket, abort, {Ref, Reason}} ->
- {error, Reason}
-
- after Timeout ->
- cancel(SockRef, connect, Ref),
- {error, timeout}
- end;
-
-
- {error, _} = ERROR ->
- ERROR
- end.
-
-
-
-%% ===========================================================================
-%%
-%% listen - listen for connections on a socket
-%%
-
--spec listen(Socket) -> ok | {error, Reason} when
- Socket :: socket(),
- Reason :: term().
-
-listen(Socket) ->
- listen(Socket, ?ESOCK_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).
-
-
-
-
-%% ===========================================================================
-%%
-%% accept, accept4 - accept a connection on a socket
-%%
-
--spec accept(LSocket) -> {ok, Socket} | {error, Reason} when
- LSocket :: socket(),
- Socket :: socket(),
- Reason :: term().
-
-accept(Socket) ->
- accept(Socket, ?ESOCK_ACCEPT_TIMEOUT_DEFAULT).
-
--spec accept(LSocket, nowait) ->
- {ok, Socket} |
- {select, SelectInfo} |
- {error, Reason} when
- LSocket :: socket(),
- Socket :: socket(),
- SelectInfo :: select_info(),
- Reason :: term()
- ; (LSocket, Timeout) -> {ok, Socket} | {error, Reason} when
- LSocket :: socket(),
- Timeout :: timeout(),
- Socket :: socket(),
- Reason :: term().
-
-accept(#socket{ref = LSockRef}, Timeout) ->
- try
- do_accept(LSockRef, deadline(Timeout))
- catch
- throw:ERROR ->
- ERROR
- end.
-
-do_accept(LSockRef, Deadline) ->
- AccRef = make_ref(),
- case nif_accept(LSockRef, AccRef) of
-
- {ok, SockRef} ->
- Socket = #socket{ref = SockRef},
- {ok, Socket};
-
-
- {error, eagain} when (Deadline =:= nowait) ->
- ?SELECT(accept, AccRef);
-
- {error, eagain} ->
- %% Each call is non-blocking, but even then it takes
- %% *some* time, so just to be sure, recalculate before
- %% the receive.
- Timeout = timeout(Deadline),
- receive
- {?ESOCK_TAG, #socket{ref = LSockRef}, select, AccRef} ->
- do_accept(LSockRef, Deadline);
-
- {?ESOCK_TAG, _Socket, abort, {AccRef, Reason}} ->
- {error, Reason}
-
- after Timeout ->
- cancel(LSockRef, accept, AccRef),
- {error, timeout}
- end;
-
-
- {error, _} = ERROR ->
- cancel(LSockRef, accept, AccRef), % Just to be on the safe side...
- ERROR
- end.
-
-
-
-%% ===========================================================================
-%%
-%% 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, ?ESOCK_SEND_FLAGS_DEFAULT, ?ESOCK_SEND_TIMEOUT_DEFAULT).
-
--spec send(Socket, Data, Flags) -> ok | {error, Reason} when
- Socket :: socket(),
- Data :: iodata(),
- Flags :: send_flags(),
- Reason :: term();
- (Socket, Data, Timeout :: nowait) ->
- ok |
- {ok, {binary(), SelectInfo}} |
- {select, SelectInfo} |
- {ok, {RestData, SelectInfo}} |
- {error, Reason} when
- Socket :: socket(),
- Data :: iodata(),
- RestData :: binary(),
- SelectInfo :: select_info(),
- 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, ?ESOCK_SEND_TIMEOUT_DEFAULT);
-send(Socket, Data, Timeout) ->
- send(Socket, Data, ?ESOCK_SEND_FLAGS_DEFAULT, Timeout).
-
--spec send(Socket, Data, Flags, nowait) -> ok |
- {select, SelectInfo} |
- {ok, {RestData, SelectInfo}} |
- {error, Reason} when
- Socket :: socket(),
- Data :: iodata(),
- Flags :: send_flags(),
- RestData :: binary(),
- SelectInfo :: select_info(),
- Reason :: term()
- ; (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{ref = SockRef}, Data, Flags, Timeout)
- when is_binary(Data), is_list(Flags) ->
- To = undefined,
- try
- begin
- EFlags = enc_send_flags(Flags),
- Deadline = deadline(Timeout),
- send_common(SockRef, Data, To, EFlags, Deadline, send)
- end
- catch
- throw:ERROR ->
- ERROR
- end.
-
-send_common(SockRef, Data, To, EFlags, Deadline, SendName) ->
- SendRef = make_ref(),
-
- case
- case SendName of
- send ->
- nif_send(SockRef, SendRef, Data, EFlags);
- sendto ->
- nif_sendto(SockRef, SendRef, Data, To, EFlags)
- end
- of
-
- ok -> ok;
-
-
- {ok, Written} when (Deadline =:= nowait) ->
- %% We are partially done, but the user don't want to wait (here)
- %% for completion
- <<_:Written/binary, Rest/binary>> = Data,
- {ok, {Rest, ?SELECT_INFO(SendName, SendRef)}};
-
- {ok, Written} ->
- %% We are partially done, wait for continuation
- Timeout = timeout(Deadline),
- receive
- {?ESOCK_TAG, _Socket, select, SendRef}
- when (Written > 0) ->
- <<_:Written/binary, Rest/binary>> = Data,
- send_common(
- SockRef, Rest, To, EFlags, Deadline, SendName);
-
- {?ESOCK_TAG, _Socket, select, SendRef} ->
- send_common(
- SockRef, Data, To, EFlags, Deadline, SendName);
-
- {?ESOCK_TAG, _Socket, abort, {SendRef, Reason}} ->
- {error, {Reason, size(Data)}}
-
- after Timeout ->
- _ = cancel(SockRef, SendName, SendRef),
- {error, {timeout, size(Data)}}
- end;
-
-
- {error, exbusy} = Error when Deadline =:= nowait -> Error;
-
- {error, exbusy = Reason} ->
- %% Internal error:
- %% we called send, got eagain, and called send again
- %% - without waiting for select message
- erlang:error(Reason);
-
-
- {error, eagain} when (Deadline =:= nowait) ->
- ?SELECT(SendName, SendRef);
-
- {error, eagain} ->
- Timeout = timeout(Deadline),
- receive
- {?ESOCK_TAG, _Socket, select, SendRef} ->
- send_common(
- SockRef, Data, To, EFlags, Deadline, SendName);
-
- {?ESOCK_TAG, _Socket, abort, {SendRef, Reason}} ->
- {error, {Reason, size(Data)}}
-
- after Timeout ->
- _ = cancel(SockRef, SendName, SendRef),
- {error, {timeout, size(Data)}}
- end;
-
-
- {error, Reason} ->
- {error, {Reason, size(Data)}}
- end.
-
-
-%% ---------------------------------------------------------------------------
-%%
-
--spec sendto(Socket, Data, Dest) ->
- ok | {error, Reason} when
- Socket :: socket(),
- Data :: binary(),
- Dest :: sockaddr(),
- Reason :: term().
-
-sendto(Socket, Data, Dest) ->
- sendto(Socket, Data, Dest, ?ESOCK_SENDTO_FLAGS_DEFAULT).
-
--spec sendto(Socket, Data, Dest, Flags) -> ok | {error, Reason} when
- Socket :: socket(),
- Data :: binary(),
- Dest :: sockaddr(),
- Flags :: send_flags(),
- Reason :: term()
- ; (Socket, Data, Dest, Timeout :: nowait) -> ok |
- {select, SelectInfo} |
- {error, Reason} when
- Socket :: socket(),
- Data :: iodata(),
- Dest :: sockaddr(),
- SelectInfo :: select_info(),
- Reason :: term()
- ; (Socket, Data, Dest, Timeout) -> ok | {error, Reason} when
- Socket :: socket(),
- Data :: iodata(),
- Dest :: sockaddr(),
- Timeout :: timeout(),
- Reason :: term().
-
-sendto(Socket, Data, Dest, Flags) when is_list(Flags) ->
- sendto(Socket, Data, Dest, Flags, ?ESOCK_SENDTO_TIMEOUT_DEFAULT);
-sendto(Socket, Data, Dest, Timeout) ->
- sendto(Socket, Data, Dest, ?ESOCK_SENDTO_FLAGS_DEFAULT, Timeout).
-
-
--spec sendto(Socket, Data, Dest, Flags, nowait) ->
- ok |
- {ok, {binary(), SelectInfo}} |
- {select, SelectInfo} |
- {error, Reason} when
- Socket :: socket(),
- Data :: binary(),
- Dest :: sockaddr(),
- Flags :: send_flags(),
- SelectInfo :: select_info(),
- Reason :: term()
- ; (Socket, Data, Dest, Flags, Timeout) -> ok | {error, Reason} when
- Socket :: socket(),
- Data :: binary(),
- Dest :: sockaddr(),
- Flags :: send_flags(),
- Timeout :: timeout(),
- Reason :: term().
-
-sendto(Socket, Data, Dest, Flags, Timeout) when is_list(Data) ->
- Bin = erlang:list_to_binary(Data),
- sendto(Socket, Bin, Dest, Flags, Timeout);
-sendto(#socket{ref = SockRef}, Data, Dest, Flags, Timeout)
- when is_binary(Data), is_list(Flags) ->
- try
- begin
- To = ensure_sockaddr(Dest),
- EFlags = enc_send_flags(Flags),
- Deadline = deadline(Timeout),
- send_common(SockRef, Data, To, EFlags, Deadline, sendto)
- end
- catch
- throw:ERROR ->
- ERROR
- end.
-
-
-%% ---------------------------------------------------------------------------
-%%
-%% 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) ->
- ok |
- {ok, Remaining} |
- {error, Reason} when
- Socket :: socket(),
- MsgHdr :: msghdr(),
- Remaining :: erlang:iovec(),
- Reason :: term().
-
-sendmsg(Socket, MsgHdr) ->
- sendmsg(Socket, MsgHdr,
- ?ESOCK_SENDMSG_FLAGS_DEFAULT, ?ESOCK_SENDMSG_TIMEOUT_DEFAULT).
-
-
--spec sendmsg(Socket, MsgHdr, Flags) -> ok | {error, Reason} when
- Socket :: socket(),
- MsgHdr :: msghdr(),
- Flags :: send_flags(),
- Reason :: term();
- (Socket, MsgHdr, Timeout :: nowait) ->
- ok |
- {ok, Remaining} |
- {error, Reason} when
- Socket :: socket(),
- MsgHdr :: msghdr(),
- Remaining :: erlang:iovec(),
- 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, ?ESOCK_SENDMSG_TIMEOUT_DEFAULT);
-sendmsg(Socket, MsgHdr, Timeout) ->
- sendmsg(Socket, MsgHdr, ?ESOCK_SENDMSG_FLAGS_DEFAULT, Timeout).
-
-
--spec sendmsg(Socket, MsgHdr, Flags, nowait) ->
- ok |
- {ok, Remaining} |
- {error, Reason} when
- Socket :: socket(),
- MsgHdr :: msghdr(),
- Flags :: send_flags(),
- Remaining :: erlang:iovec(),
- Reason :: term()
- ; (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), is_list(Flags) ->
- try
- begin
- M = ensure_msghdr(MsgHdr),
- EFlags = enc_send_flags(Flags),
- Deadline = deadline(Timeout),
- do_sendmsg(SockRef, M, EFlags, Deadline)
- end
- catch
- throw:ERROR ->
- ERROR
- end.
-
-
-do_sendmsg(SockRef, MsgHdr, EFlags, Deadline) ->
-
- SendRef = make_ref(),
-
- case nif_sendmsg(SockRef, SendRef, MsgHdr, EFlags) of
- ok ->
- %% 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 need to cancel this partial write.
- %%
- _ = cancel(SockRef, sendmsg, SendRef),
- {ok, do_sendmsg_rest(maps:get(iov, MsgHdr), Written)};
-
-
- {error, exbusy} = Error when Deadline =:= nowait -> Error;
-
- {error, exbusy = Reason} ->
- %% Internal error:
- %% we called send, got eagain, and called send again
- %% - without waiting for select message
- erlang:error(Reason);
-
-
- {error, eagain} when (Deadline =:= nowait) ->
- ?SELECT(sendmsg, SendRef);
-
- {error, eagain} ->
- Timeout = timeout(Deadline),
- receive
- {?ESOCK_TAG, #socket{ref = SockRef}, select, SendRef} ->
- do_sendmsg(SockRef, MsgHdr, EFlags, Deadline);
-
- {?ESOCK_TAG, _Socket, abort, {SendRef, Reason}} ->
- {error, Reason}
-
- after Timeout ->
- _ = cancel(SockRef, sendmsg, SendRef),
- {error, timeout}
- end;
-
-
- {error, _} = ERROR ->
- 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, addr := Addr} = M)
- when is_list(IOV) andalso (IOV =/= []) ->
- M#{iov => erlang:iolist_to_iovec(IOV), addr => ensure_sockaddr(Addr)};
-ensure_msghdr(#{iov := IOV} = M)
- when is_list(IOV) andalso (IOV =/= []) ->
- M#{iov => erlang:iolist_to_iovec(IOV)};
-ensure_msghdr(_) ->
- einval().
-
-
-
-%% ===========================================================================
-%%
-%% 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.
-
--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,
- ?ESOCK_RECV_FLAGS_DEFAULT,
- ?ESOCK_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 :: nowait) -> {ok, Data} |
- {select, SelectInfo} |
- {ok, {Data, SelectInfo}} |
- {error, Reason} when
- Socket :: socket(),
- Length :: non_neg_integer(),
- Data :: binary(),
- SelectInfo :: select_info(),
- 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, ?ESOCK_RECV_TIMEOUT_DEFAULT);
-recv(Socket, Length, Timeout) ->
- recv(Socket, Length, ?ESOCK_RECV_FLAGS_DEFAULT, Timeout).
-
--spec recv(Socket, Length, Flags, nowait) -> {ok, Data} |
- {select, SelectInfo} |
- {ok, {Data, SelectInfo}} |
- {error, Reason} when
- Socket :: socket(),
- Length :: non_neg_integer(),
- Flags :: recv_flags(),
- Data :: binary(),
- SelectInfo :: select_info(),
- Reason :: term()
- ; (Socket, Length, Flags, Timeout) -> {ok, Data} |
- {error, Reason} when
- Socket :: socket(),
- Length :: non_neg_integer(),
- Flags :: recv_flags(),
- Timeout :: timeout(),
- Data :: binary(),
- Reason :: term().
-
-recv(#socket{ref = SockRef}, Length, Flags, Timeout)
- when is_integer(Length), Length >= 0, is_list(Flags) ->
- try
- EFlags = enc_recv_flags(Flags),
- Deadline = deadline(Timeout),
- do_recv(SockRef, Length, EFlags, Deadline, <<>>)
- catch
- throw:ERROR ->
- ERROR
- end.
-
-%% We will only recurse with Length == 0 if Length is 0,
-%% so Length == 0 means to return all available data also when recursing
-%%
-%% Note that the Deadline value of 'nowait' has a special meaning. It means
-%% that we will either return with data or with the with {error, NNNN}. In
-%% wich case the caller will receive a select message at some later time.
-%%
-do_recv(SockRef, Length, EFlags, Deadline, Acc) ->
-
- RecvRef = make_ref(),
- case nif_recv(SockRef, RecvRef, Length, EFlags) of
-
- {ok, true = _Complete, Bin} ->
- {ok, bincat(Acc, Bin)};
-
-
- %% 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 ->
- Timeout = timeout(Deadline),
- if
- 0 < Timeout ->
- do_recv(
- SockRef, Length, EFlags, Deadline, bincat(Acc, Bin));
- true ->
- {ok, bincat(Acc, Bin)}
- end;
-
- %% Did not get all the user asked for, but the user also
- %% specified 'nowait', so deliver what we got and the
- %% select info.
- {ok, false = _Completed, Bin} when Deadline =:= nowait ->
- {ok, {bincat(Acc, Bin), ?SELECT_INFO(recv, RecvRef)}};
-
- {ok, false = _Completed, Bin} ->
- %% We got a chunk of it!
- Timeout = timeout(Deadline),
- receive
- {?ESOCK_TAG, #socket{ref = SockRef}, select, RecvRef} ->
- if
- 0 < Timeout ->
- do_recv(
- SockRef, Length - byte_size(Bin), EFlags,
- Deadline, bincat(Acc, Bin));
- true ->
- {error, {timeout, bincat(Acc, Bin)}}
- end;
-
- {?ESOCK_TAG, _Socket, abort, {RecvRef, Reason}} ->
- {error, Reason}
-
- after Timeout ->
- cancel(SockRef, recv, RecvRef),
- {error, {timeout, bincat(Acc, Bin)}}
- end;
-
-
- {error, exbusy} = Error when Deadline =:= nowait -> Error;
-
- {error, exbusy = Reason} ->
- %% Internal error:
- %% we called recv, got eagain, and called recv again
- %% - without waiting for select message
- erlang:error(Reason);
-
-
- %% The user does not want to wait!
- %% The user will be informed that there is something to read
- %% via the select socket message (see below).
- {error, eagain} when Deadline =:= nowait ->
- if
- byte_size(Acc) =:= 0 ->
- ?SELECT(recv, RecvRef);
- true ->
- {ok, {Acc, ?SELECT_INFO(recv, RecvRef)}}
- end;
-
-
- %% We return with the accumulated binary (if its non-empty)
- {error, eagain} when Length =:= 0, 0 < byte_size(Acc) ->
- cancel(SockRef, recv, RecvRef),
- {ok, Acc};
-
- {error, eagain} ->
- %% There is nothing just now, but we will be notified when there
- %% is something to read (a select message).
- Timeout = timeout(Deadline),
- receive
- {?ESOCK_TAG, #socket{ref = SockRef}, select, RecvRef} ->
- if
- 0 < Timeout ->
- do_recv(
- SockRef, Length, EFlags, Deadline, Acc);
- 0 < byte_size(Acc) ->
- {error, {timeout, Acc}};
- true ->
- {error, timeout}
- end;
-
- {?ESOCK_TAG, _Socket, abort, {RecvRef, Reason}} ->
- {error, Reason}
-
- after Timeout ->
- cancel(SockRef, recv, RecvRef),
- {error, timeout}
- end;
-
-
- {error, _} = ERROR when byte_size(Acc) =:= 0 ->
- ERROR;
-
- {error, Reason} ->
- {error, {Reason, Acc}}
-
- end.
-
-
-
-%% ---------------------------------------------------------------------------
-%%
-%% 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 (OS) buffers, so another recvfrom call
-%% 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,
- ?ESOCK_RECV_FLAGS_DEFAULT,
- ?ESOCK_RECV_TIMEOUT_DEFAULT).
-
--spec recvfrom(Socket, Flags, nowait) ->
- {ok, {Source, Data}} |
- {select, SelectInfo} |
- {error, Reason} when
- Socket :: socket(),
- Flags :: recv_flags(),
- Source :: sockaddr() | undefined,
- Data :: binary(),
- SelectInfo :: select_info(),
- Reason :: term()
- ; (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, nowait) ->
- {ok, {Source, Data}} |
- {select, SelectInfo} |
- {error, Reason} when
- Socket :: socket(),
- BufSz :: non_neg_integer(),
- Source :: sockaddr() | undefined,
- Data :: binary(),
- SelectInfo :: select_info(),
- 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);
-recvfrom(Socket, BufSz, Flags) when is_list(Flags) ->
- recvfrom(Socket, BufSz, Flags, ?ESOCK_RECV_TIMEOUT_DEFAULT);
-recvfrom(Socket, BufSz, Timeout) ->
- recvfrom(Socket, BufSz, ?ESOCK_RECV_FLAGS_DEFAULT, Timeout).
-
--spec recvfrom(Socket, BufSz, Flags, nowait) ->
- {ok, {Source, Data}} |
- {select, SelectInfo} |
- {error, Reason} when
- Socket :: socket(),
- BufSz :: non_neg_integer(),
- Flags :: recv_flags(),
- Source :: sockaddr() | undefined,
- Data :: binary(),
- SelectInfo :: select_info(),
- Reason :: term()
- ; (Socket, BufSz, Flags, Timeout) ->
- {ok, {Source, Data}} |
- {error, Reason} when
- Socket :: socket(),
- BufSz :: non_neg_integer(),
- Flags :: recv_flags(),
- Timeout :: timeout(),
- Source :: sockaddr() | undefined,
- Data :: binary(),
- Reason :: term().
-
-recvfrom(#socket{ref = SockRef}, BufSz, Flags, Timeout)
- when is_integer(BufSz), 0 =< BufSz, is_list(Flags) ->
- try
- EFlags = enc_recv_flags(Flags),
- Deadline = deadline(Timeout),
- do_recvfrom(SockRef, BufSz, EFlags, Deadline)
- catch
- throw:ERROR ->
- ERROR
- end.
-
-do_recvfrom(SockRef, BufSz, EFlags, Deadline) ->
-
- RecvRef = make_ref(),
- case nif_recvfrom(SockRef, RecvRef, BufSz, EFlags) of
-
- {ok, {_Source, _NewData}} = OK ->
- OK;
-
-
- {error, exbusy} = Error when Deadline =:= nowait -> Error;
-
- {error, exbusy = Reason} ->
- %% Internal error:
- %% we called recvfrom, got eagain, and called recvfrom again
- %% - without waiting for select message
- erlang:error(Reason);
-
-
- {error, eagain} when Deadline =:= nowait ->
- ?SELECT(recvfrom, RecvRef);
-
- {error, eagain} ->
- %% There is nothing just now, but we will be notified when there
- %% is something to read (a select message).
- Timeout = timeout(Deadline),
- receive
- {?ESOCK_TAG, #socket{ref = SockRef}, select, RecvRef} ->
- do_recvfrom(SockRef, BufSz, EFlags, Deadline);
-
- {?ESOCK_TAG, _Socket, abort, {RecvRef, Reason}} ->
- {error, Reason}
-
- after Timeout ->
- cancel(SockRef, recvfrom, RecvRef),
- {error, timeout}
- end;
-
-
- {error, _Reason} = ERROR ->
- ERROR
-
- end.
-
-
-%% ---------------------------------------------------------------------------
-%%
-
--spec recvmsg(Socket) -> {ok, MsgHdr} | {error, Reason} when
- Socket :: socket(),
- MsgHdr :: msghdr(),
- Reason :: term().
-
-recvmsg(Socket) ->
- recvmsg(Socket, 0, 0,
- ?ESOCK_RECV_FLAGS_DEFAULT, ?ESOCK_RECV_TIMEOUT_DEFAULT).
-
--spec recvmsg(Socket, Flags) -> {ok, MsgHdr} | {error, Reason} when
- Socket :: socket(),
- Flags :: recv_flags(),
- MsgHdr :: msghdr(),
- Reason :: term()
- ; (Socket, Timeout :: nowait) -> {ok, MsgHdr} |
- {select, SelectInfo} |
- {error, Reason} when
- Socket :: socket(),
- MsgHdr :: msghdr(),
- SelectInfo :: select_info(),
- 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, ?ESOCK_RECV_TIMEOUT_DEFAULT);
-recvmsg(Socket, Timeout) ->
- recvmsg(Socket, 0, 0, ?ESOCK_RECV_FLAGS_DEFAULT, Timeout).
-
--spec recvmsg(Socket, Flags, nowait) -> {ok, MsgHdr} |
- {select, SelectInfo} |
- {error, Reason} when
- Socket :: socket(),
- Flags :: recv_flags(),
- MsgHdr :: msghdr(),
- SelectInfo :: select_info(),
- Reason :: term()
- ; (Socket, Flags, Timeout) -> {ok, MsgHdr} | {error, Reason} when
- Socket :: socket(),
- 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) when is_list(Flags) ->
- recvmsg(Socket, 0, 0, Flags, Timeout);
-recvmsg(Socket, BufSz, CtrlSz) when is_integer(BufSz), is_integer(CtrlSz) ->
- recvmsg(Socket, BufSz, CtrlSz,
- ?ESOCK_RECV_FLAGS_DEFAULT, ?ESOCK_RECV_TIMEOUT_DEFAULT).
-
-
--spec recvmsg(Socket,
- BufSz, CtrlSz,
- Flags, nowait) -> {ok, MsgHdr} |
- {select, SelectInfo} |
- {error, Reason} when
- Socket :: socket(),
- BufSz :: non_neg_integer(),
- CtrlSz :: non_neg_integer(),
- Flags :: recv_flags(),
- MsgHdr :: msghdr(),
- SelectInfo :: select_info(),
- Reason :: term()
- ; (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), 0 =< BufSz,
- is_integer(CtrlSz), 0 =< CtrlSz,
- is_list(Flags) ->
- try
- EFlags = enc_recv_flags(Flags),
- Deadline = deadline(Timeout),
- do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Deadline)
- catch
- throw:ERROR ->
- ERROR
- end.
-
-do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Deadline) ->
-
- RecvRef = make_ref(),
- case nif_recvmsg(SockRef, RecvRef, BufSz, CtrlSz, EFlags) of
-
- {ok, _MsgHdr} = OK ->
- OK;
-
-
- {error, exbusy} = Error when Deadline =:= nowait -> Error;
-
- {error, exbusy = Reason} ->
- %% Internal error:
- %% we called recvmsg, got eagain, and called recvmsg again
- %% - without waiting for select message
- erlang:error(Reason);
-
-
- {error, eagain} when Deadline =:= nowait ->
- ?SELECT(recvmsg, RecvRef);
-
- {error, eagain} ->
- %% There is nothing just now, but we will be notified when there
- %% is something to read (a select message).
- Timeout = timeout(Deadline),
- receive
- {?ESOCK_TAG, #socket{ref = SockRef}, select, RecvRef} ->
- do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Deadline);
-
- {?ESOCK_TAG, _Socket, abort, {RecvRef, Reason}} ->
- {error, Reason}
-
- after Timeout ->
- cancel(SockRef, recvmsg, RecvRef),
- {error, timeout}
- end;
-
-
- {error, _Reason} = ERROR ->
- ERROR
-
- end.
-
-
-
-%% ===========================================================================
-%%
-%% close - close a file descriptor
-%%
-%% Closing a socket is a two stage rocket (because of linger).
-%% We need to perform the actual socket close while in BLOCKING mode.
-%% But that would hang the entire VM, so what we do is divide the
-%% close in two steps:
-%% 1) nif_close + the socket_stop (nif) callback function
-%% This is for everything that can be done safely NON-BLOCKING.
-%% 2) nif_finalize_close which is executed by a *dirty* scheduler
-%% Before we call the socket close function, we set the socket
-%% BLOCKING. Thereby linger is handled properly.
-
--spec close(Socket) -> ok | {error, Reason} when
- Socket :: socket(),
- Reason :: term().
-
-close(#socket{ref = SockRef}) ->
- case nif_close(SockRef) of
- ok ->
- nif_finalize_close(SockRef);
- {ok, CloseRef} ->
- %% We must wait for the socket_stop callback function to
- %% complete its work
- receive
- {?ESOCK_TAG, #socket{ref = SockRef}, close, CloseRef} ->
- nif_finalize_close(SockRef)
- end;
- {error, _} = ERROR ->
- ERROR
- end.
-
-
-
-
-%% ===========================================================================
-%%
-%% 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
- nif_shutdown(SockRef, enc_shutdown_how(How))
- catch
- throw:T ->
- T;
- %% <WIN32-TEMPORARY>
- error:notsup:S ->
- erlang:raise(error, notsup, S);
- %% </WIN32-TEMPORARY>
- error:Reason ->
- {error, Reason}
- end.
-
-
-
-
-%% ===========================================================================
-%%
-%% 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...
-%%
-%% <KOLLA>
-%%
-%% WE NEED TO MAKE SURE THAT THE USER DOES NOT MAKE US BLOCKING
-%% AS MUCH OF THE CODE EXPECTS TO BE NON-BLOCKING!!
-%%
-%% </KOLLA>
-
--spec setopt(Socket, otp, otp_socket_option(), Value) -> ok | {error, Reason} when
- Socket :: socket(),
- Value :: term(),
- Reason :: term()
- ; (Socket, socket, socket_option(), Value) -> ok | {error, Reason} when
- Socket :: socket(),
- Value :: term(),
- Reason :: term()
- ; (Socket, ip, ip_socket_option(), Value) -> ok | {error, Reason} when
- Socket :: socket(),
- Value :: term(),
- Reason :: term()
- ; (Socket, ipv6, ipv6_socket_option(), Value) -> ok | {error, Reason} when
- Socket :: socket(),
- Value :: term(),
- Reason :: term()
- ; (Socket, tcp, tcp_socket_option(), Value) -> ok | {error, Reason} when
- Socket :: socket(),
- Value :: term(),
- Reason :: term()
- ; (Socket, udp, udp_socket_option(), Value) -> ok | {error, Reason} when
- Socket :: socket(),
- Value :: term(),
- Reason :: term()
- ; (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) ->
- try
- begin
- {Domain, Type, Proto} = which_dtp(SockRef),
- {EIsEncoded, ELevel} = enc_setopt_level(Level),
- EKey = enc_setopt_key(Level, Key, Domain, Type, Proto),
- EVal = enc_setopt_value(Level, Key, Value, Domain, Type, Proto),
- nif_setopt(SockRef, EIsEncoded, ELevel, EKey, EVal)
- end
- catch
- throw:ERROR ->
- ERROR
- 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...
-%%
-%% 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, otp, otp_socket_option()) -> {ok, Value} | {error, Reason} when
- Socket :: socket(),
- Value :: term(),
- Reason :: term()
- ; (Socket, socket, socket_option()) -> {ok, Value} | {error, Reason} when
- Socket :: socket(),
- Value :: term(),
- Reason :: term()
- ; (Socket, ip, ip_socket_option()) -> {ok, Value} | {error, Reason} when
- Socket :: socket(),
- Value :: term(),
- Reason :: term()
- ; (Socket, ipv6, ipv6_socket_option()) -> {ok, Value} | {error, Reason} when
- Socket :: socket(),
- Value :: term(),
- Reason :: term()
- ; (Socket, tcp, tcp_socket_option()) -> {ok, Value} | {error, Reason} when
- Socket :: socket(),
- Value :: term(),
- Reason :: term()
- ; (Socket, udp, udp_socket_option()) -> {ok, Value} | {error, Reason} when
- Socket :: socket(),
- Value :: term(),
- Reason :: term()
- ; (Socket, sctp, sctp_socket_option()) -> {ok, Value} | {error, Reason} when
- Socket :: socket(),
- Value :: term(),
- Reason :: term()
- ; (Socket, Level, Key) -> ok | {ok, Value} | {error, Reason} when
- Socket :: socket(),
- Level :: integer(),
- Key :: {NativeOpt, ValueSize},
- NativeOpt :: integer(),
- ValueSize :: int | bool | non_neg_integer(),
- Value :: term(),
- Reason :: term().
-
-getopt(#socket{ref = SockRef}, Level, Key) ->
- try
- begin
- {Domain, Type, Proto} = which_dtp(SockRef),
- {EIsEncoded, ELevel} = enc_getopt_level(Level),
- EKey = enc_getopt_key(Level, Key, Domain, Type, Proto),
- %% We may need to decode the value (for the same reason
- %% we (may have) 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, Proto),
- {ok, Val};
- {error, _} = E ->
- E
- end
- end
- catch
- throw:ERROR ->
- ERROR
- end.
-
-
-%% 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,
- ?ESOCK_OPT_LEVEL_OTP, ?ESOCK_OPT_OTP_DOMAIN) of
- {ok, Domain} ->
- if
- is_atom(Domain) ->
- Domain;
- is_integer(Domain) ->
- invalid_domain(Domain)
- end;
- {error, _} = ERROR ->
- throw(ERROR)
- end.
-
-
-%%%-spec which_type(SockRef) -> Type when
-%%% SockRef :: reference(),
-%%% Type :: type().
-%%%
-%%%which_type(SockRef) ->
-%%% case nif_getopt(SockRef, true,
-%%% ?ESOCK_OPT_LEVEL_OTP, ?ESOCK_OPT_OTP_TYPE) of
-%%% {ok, Type} ->
-%%% if
-%%% is_atom(Type) ->
-%%% Type;
-%%% is_integer(Type) ->
-%%% invalid_type(Type)
-%%% end;
-%%% {error, _} = ERROR ->
-%%% throw(ERROR)
-%%% end.
-%%%
-%%%-spec which_protocol(SockRef) -> Protocol when
-%%% SockRef :: reference(),
-%%% Protocol :: protocol().
-%%%
-%%%which_protocol(SockRef) ->
-%%% case nif_getopt(SockRef, true,
-%%% ?ESOCK_OPT_LEVEL_OTP, ?ESOCK_OPT_OTP_PROTOCOL) of
-%%% {ok, Proto} ->
-%%% if
-%%% is_atom(Proto) ->
-%%% Proto;
-%%% is_integer(Proto) ->
-%%% invalid_protocol(Proto)
-%%% end;
-%%% {error, _} = ERROR ->
-%%% throw(ERROR)
-%%% end.
-
-which_dtp(SockRef) ->
- case
- nif_getopt(
- SockRef, true, ?ESOCK_OPT_LEVEL_OTP, ?ESOCK_OPT_OTP_DTP)
- of
- {ok, {Domain, Type, Proto} = DTP} ->
- if
- is_integer(Domain) ->
- invalid_domain(Domain);
- is_integer(Type) ->
- invalid_type(Type);
- is_integer(Proto) ->
- invalid_protocol(Proto);
- is_atom(Domain), is_atom(Type), is_atom(Proto) ->
- DTP
- end;
- {error, _} = ERROR ->
- throw(ERROR)
- end.
-
-
-
-%% ===========================================================================
-%%
-%% 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).
-
-
-%% ===========================================================================
-%%
-%% cancel - cancel an operation resulting in a select
-%%
-%% A call to accept, recv/recvfrom/recvmsg and send/sendto/sendmsg
-%% can result in a select if they are called with the Timeout argument
-%% set to nowait. This is indicated by the return of the select-info.
-%% Such a operation can be cancelled by calling this function.
-%%
-
--spec cancel(Socket, SelectInfo) -> ok | {error, Reason} when
- Socket :: socket(),
- SelectInfo :: select_info(),
- Reason :: term().
-
-cancel(#socket{ref = SockRef}, ?SELECT_INFO(Tag, Ref)) ->
- cancel(SockRef, Tag, Ref).
-
-
-
-%% ===========================================================================
-%%
-%% Encode / decode
-%%
-%% ===========================================================================
-
-%% File names has to be encoded according to
-%% the native file encoding
-%%
-enc_path(Path) ->
- %% These are all BIFs - will not cause code loading
- unicode:characters_to_binary(Path, file:native_name_encoding()).
-
-%% -spec enc_domain(Domain) -> non_neg_integer() when
-%% Domain :: domain().
-
-enc_domain(local) -> ?ESOCK_DOMAIN_LOCAL;
-enc_domain(inet) -> ?ESOCK_DOMAIN_INET;
-enc_domain(inet6) -> ?ESOCK_DOMAIN_INET6;
-enc_domain(Domain) -> invalid_domain(Domain).
-
-%% -spec enc_type(Type) -> non_neg_integer() when
-%% Type :: type().
-
-enc_type(stream) -> ?ESOCK_TYPE_STREAM;
-enc_type(dgram) -> ?ESOCK_TYPE_DGRAM;
-enc_type(raw) -> ?ESOCK_TYPE_RAW;
-enc_type(seqpacket) -> ?ESOCK_TYPE_SEQPACKET;
-enc_type(Type) -> invalid_type(Type).
-
--spec enc_protocol(Protocol) -> non_neg_integer() |
- {raw, non_neg_integer()} when
- Protocol :: protocol().
-
-enc_protocol(default) -> ?ESOCK_PROTOCOL_DEFAULT;
-enc_protocol(ip) -> ?ESOCK_PROTOCOL_IP;
-enc_protocol(tcp) -> ?ESOCK_PROTOCOL_TCP;
-enc_protocol(udp) -> ?ESOCK_PROTOCOL_UDP;
-enc_protocol(sctp) -> ?ESOCK_PROTOCOL_SCTP;
-enc_protocol(icmp) -> ?ESOCK_PROTOCOL_ICMP;
-enc_protocol(igmp) -> ?ESOCK_PROTOCOL_IGMP;
-enc_protocol({raw, P} = RAW) when is_integer(P) -> RAW;
-enc_protocol(Proto) ->
- invalid_protocol(Proto).
-
-
--spec enc_send_flags(Flags) -> non_neg_integer() when
- Flags :: send_flags().
-
-enc_send_flags(Flags) ->
- EFlags = [{confirm, ?ESOCK_SEND_FLAG_CONFIRM},
- {dontroute, ?ESOCK_SEND_FLAG_DONTROUTE},
- {eor, ?ESOCK_SEND_FLAG_EOR},
- {more, ?ESOCK_SEND_FLAG_MORE},
- {nosignal, ?ESOCK_SEND_FLAG_NOSIGNAL},
- {oob, ?ESOCK_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, ?ESOCK_RECV_FLAG_CMSG_CLOEXEC},
- {errqueue, ?ESOCK_RECV_FLAG_ERRQUEUE},
- {oob, ?ESOCK_RECV_FLAG_OOB},
- {peek, ?ESOCK_RECV_FLAG_PEEK},
- {trunc, ?ESOCK_RECV_FLAG_TRUNC}],
- 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).
-
-
-%% +++ Encode setopt level +++
-
--spec enc_setopt_level(Level) -> {IsEncoded, EncodedLevel} when
- Level :: sockopt_level(),
- IsEncoded :: boolean(),
- EncodedLevel :: integer().
-
-enc_setopt_level(otp) ->
- {true, ?ESOCK_OPT_LEVEL_OTP};
-enc_setopt_level(socket) ->
- {true, ?ESOCK_OPT_LEVEL_SOCKET};
-enc_setopt_level(ip) ->
- {true, ?ESOCK_OPT_LEVEL_IP};
-enc_setopt_level(ipv6) ->
- {true, ?ESOCK_OPT_LEVEL_IPV6};
-enc_setopt_level(tcp) ->
- {true, ?ESOCK_OPT_LEVEL_TCP};
-enc_setopt_level(udp) ->
- {true, ?ESOCK_OPT_LEVEL_UDP};
-enc_setopt_level(sctp) ->
- {true, ?ESOCK_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) ->
- {false, L}.
-
-
-%% +++ 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 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).
-
--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) ->
- V;
-enc_setopt_value(otp, controlling_process, V, _, _, _) when is_pid(V) ->
- V;
-enc_setopt_value(otp, rcvbuf, V, _, _, _) when (V =:= default) ->
- 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, _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) ->
- 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, meta, V, _, _, _) ->
- 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) ->
- 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) ->
- 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(socket, oobinline, V, _D, _T, _P) when is_boolean(V) ->
- V;
-enc_setopt_value(socket, passcred, V, _D, _T, _P) when is_boolean(V) ->
- V;
-enc_setopt_value(socket, peek_off = Opt, V, _D, _T, _P) when is_integer(V) ->
- %% V;
- not_supported(Opt);
-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;
-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, 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;
-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});
-
-enc_setopt_value(ip, add_membership, #{multiaddr := MA,
- interface := IF} = V, _D, _T, _P)
- 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,
- 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(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, 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) ->
- 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
- (V =:= do) orelse
- (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;
-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, 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, recvdstaddr, 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;
-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;
-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;
-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
- (V =:= reliability) orelse
- (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;
-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});
-
-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
- (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, 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
- (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;
-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)
- when (V =:= want) orelse
- (V =:= dont) orelse
- (V =:= do) orelse
- (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, 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, recverr, V, _D, _T, _P)
- when is_boolean(V) ->
- V;
-enc_setopt_value(ipv6, recvhoplimit, V, _D, T, _P)
- when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) ->
- 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, recvtclass, V, _D, T, _P)
- when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) ->
- 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;
-enc_setopt_value(ipv6, tclass, 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) ->
- not_supported({L, Opt, V});
-
-enc_setopt_value(tcp, congestion, V, _D, T, P)
- when is_list(V) andalso
- (T =:= stream) andalso
- (P =:= tcp) ->
- V;
-enc_setopt_value(tcp, cork, V, _D, T, P)
- when is_boolean(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});
-
-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});
-
-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 (V >= 0) 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,
- 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, 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;
-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});
-
-%% 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) ->
- V.
-
-
-
-
-%% +++ Encode getopt value +++
-
-enc_getopt_level(Level) ->
- enc_setopt_level(Level).
-
-
-%% +++ Encode getopt key +++
-
-enc_getopt_key(Level, Opt, Domain, Type, Protocol) ->
- enc_sockopt_key(Level, Opt, get, Domain, Type, Protocol).
-
-
-%% +++ Decode getopt value +++
-%%
-%% For the most part, we simply let the value pass through, but for some
-%% values we may need to do an actual decode.
-%%
-
-%% This string is NULL-terminated, but the general function we use
-%% in the nif code does not know that. So, deal with it here.
-dec_getopt_value(tcp = _L, congestion = _Opt, Alg, _D, _T, _P) when is_list(Alg) ->
- {Str, _} = lists:splitwith(fun(0) -> false; (_) -> true end, Alg),
- Str;
-%% Let the user deal with the rest for now...
-dec_getopt_value(_L, _Opt, V, _D, _T, _P) ->
- V.
-
-
-
-%% +++ 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,
- Direction :: set | get,
- Opt :: otp_socket_option(),
- Domain :: domain(),
- Type :: type(),
- Protocol :: protocol();
- (Level, Opt, Direction,
- Domain, Type, Protocol) -> non_neg_integer() when
- Level :: socket,
- Direction :: set | get,
- Opt :: socket_option(),
- Domain :: domain(),
- Type :: type(),
- Protocol :: protocol();
- (Level, Opt, Direction,
- Domain, Type, Protocol) -> non_neg_integer() when
- Level :: ip,
- Direction :: set | get,
- Opt :: ip_socket_option(),
- Domain :: domain(),
- Type :: type(),
- Protocol :: protocol();
- (Level, Opt, Direction,
- Domain, Type, Protocol) -> non_neg_integer() when
- Level :: ipv6,
- Direction :: set | get,
- Opt :: ipv6_socket_option(),
- Domain :: domain(),
- Type :: type(),
- Protocol :: protocol();
- (Level, Opt, Direction,
- Domain, Type, Protocol) -> non_neg_integer() when
- Level :: tcp,
- Direction :: set | get,
- Opt :: tcp_socket_option(),
- Domain :: domain(),
- Type :: type(),
- Protocol :: protocol();
- (Level, Opt, Direction,
- Domain, Type, Protocol) -> non_neg_integer() when
- Level :: udp,
- Direction :: set | get,
- Opt :: udp_socket_option(),
- Domain :: domain(),
- Type :: type(),
- Protocol :: protocol();
- (Level, Opt, Direction,
- Domain, Type, Protocol) -> non_neg_integer() when
- Level :: sctp,
- Direction :: set | get,
- Opt :: sctp_socket_option(),
- Domain :: domain(),
- Type :: type(),
- Protocol :: protocol();
- (Level, Opt, Direction,
- Domain, Type, Protocol) -> non_neg_integer() when
- Level :: integer(),
- Direction :: set,
- Opt :: integer(),
- Domain :: domain(),
- Type :: type(),
- Protocol :: protocol();
- (Level, Opt, Direction,
- Domain, Type, Protocol) -> {NativeOpt, ValueSize} when
- Level :: integer(),
- Direction :: get,
- Opt :: {NativeOpt, ValueSize},
- NativeOpt :: integer(),
- ValueSize :: non_neg_integer() | 'int' | 'bool',
- Domain :: domain(),
- Type :: type(),
- Protocol :: protocol().
-
-
-%% +++ OTP socket options +++
-enc_sockopt_key(otp, debug, _, _, _, _) ->
- ?ESOCK_OPT_OTP_DEBUG;
-enc_sockopt_key(otp, iow, _, _, _, _) ->
- ?ESOCK_OPT_OTP_IOW;
-enc_sockopt_key(otp, controlling_process, _, _, _, _) ->
- ?ESOCK_OPT_OTP_CTRL_PROC;
-enc_sockopt_key(otp, rcvbuf, _, _, _, _) ->
- ?ESOCK_OPT_OTP_RCVBUF;
-enc_sockopt_key(otp, rcvctrlbuf, _, _, _, _) ->
- ?ESOCK_OPT_OTP_RCVCTRLBUF;
-enc_sockopt_key(otp, sndctrlbuf, _, _, _, _) ->
- ?ESOCK_OPT_OTP_SNDCTRLBUF;
-enc_sockopt_key(otp, fd, get = _Dir, _, _, _) ->
- ?ESOCK_OPT_OTP_FD;
-enc_sockopt_key(otp, meta, _, _, _, _) ->
- ?ESOCK_OPT_OTP_META;
-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) ->
- ?ESOCK_OPT_SOCK_ACCEPTCONN;
-enc_sockopt_key(socket = L, acceptconn = Opt, Dir, _D, _T, _P) ->
- not_supported({L, Opt, Dir});
-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 but not get.
-%% Maximum size of buffer for name: IFNAMSIZ
-%% So, we let the implementation decide.
-enc_sockopt_key(socket = _L, bindtodevice = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_SOCK_BINDTODEVICE;
-enc_sockopt_key(socket, broadcast = _Opt, _Dir, _D, dgram = _T, _P) ->
- ?ESOCK_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) ->
- ?ESOCK_OPT_SOCK_DEBUG;
-enc_sockopt_key(socket, domain = _Opt, get = _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_SOCK_DOMAIN;
-enc_sockopt_key(socket, dontroute = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_SOCK_DONTROUTE;
-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...
-enc_sockopt_key(socket, keepalive = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_SOCK_KEEPALIVE;
-enc_sockopt_key(socket, linger = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_SOCK_LINGER;
-enc_sockopt_key(socket = L, mark = Opt, _Dir, _D, _T, _P) ->
- not_supported({L, Opt});
-enc_sockopt_key(socket = _L, oobinline = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_SOCK_OOBINLINE;
-enc_sockopt_key(socket, passcred, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_SOCK_PASSCRED;
-enc_sockopt_key(socket = L, peek_off = Opt, _Dir, local = _D, _T, _P) ->
- %% ?ESOCK_OPT_SOCK_PEEK_OFF;
- not_supported({L, Opt});
-enc_sockopt_key(socket = L, peercred = Opt, get = _Dir, local = _D, _T, _P) ->
- not_supported({L, Opt});
-enc_sockopt_key(socket, priority = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_SOCK_PRIORITY;
-enc_sockopt_key(socket, protocol = _Opt, get = _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_SOCK_PROTOCOL;
-enc_sockopt_key(socket, rcvbuf = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_SOCK_RCVBUF;
-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) ->
- ?ESOCK_OPT_SOCK_RCVLOWAT;
-enc_sockopt_key(socket = _L, rcvtimeo = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_SOCK_RCVTIMEO;
-enc_sockopt_key(socket = _L, reuseaddr = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_SOCK_REUSEADDR;
-enc_sockopt_key(socket = _L, reuseport = _Opt, _Dir, D, _T, _P)
- when ((D =:= inet) orelse (D =:= inet6)) ->
- ?ESOCK_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) ->
- not_supported({L, Opt});
-enc_sockopt_key(socket = _L, sndbuf = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_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) ->
- ?ESOCK_OPT_SOCK_SNDLOWAT;
-enc_sockopt_key(socket = _L, sndtimeo = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_SOCK_SNDTIMEO;
-enc_sockopt_key(socket = _L, timestamp = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_SOCK_TIMESTAMP;
-enc_sockopt_key(socket = _L, type = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_SOCK_TYPE;
-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) ->
- ?ESOCK_OPT_IP_ADD_MEMBERSHIP;
-enc_sockopt_key(ip = _L, add_source_membership = _Opt, set = _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_IP_ADD_SOURCE_MEMBERSHIP;
-enc_sockopt_key(ip = _L, block_source = _Opt, set = _Dir, _D, _T, _P) ->
- ?ESOCK_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) ->
- ?ESOCK_OPT_IP_DROP_MEMBERSHIP;
-enc_sockopt_key(ip = _L, drop_source_membership = _Opt, set = _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_IP_DROP_SOURCE_MEMBERSHIP;
-%% Linux only?
-enc_sockopt_key(ip = _L, freebind = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_IP_FREEBIND;
-enc_sockopt_key(ip = _L, hdrincl = _Opt, _Dir, _D, raw = _T, _P) ->
- ?ESOCK_OPT_IP_HDRINCL;
-enc_sockopt_key(ip = _L, minttl = _Opt, _Dir, _D, raw = _T, _P) ->
- ?ESOCK_OPT_IP_MINTTL;
-enc_sockopt_key(ip = _L, msfilter = _Opt, set = _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_IP_MSFILTER;
-enc_sockopt_key(ip = _L, mtu = _Opt, get = _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_IP_MTU;
-enc_sockopt_key(ip = _L, mtu_discover = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_IP_MTU_DISCOVER;
-enc_sockopt_key(ip = _L, multicast_all = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_IP_MULTICAST_ALL;
-enc_sockopt_key(ip = _L, multicast_if = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_IP_MULTICAST_IF;
-enc_sockopt_key(ip = _L, multicast_loop = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_IP_MULTICAST_LOOP;
-enc_sockopt_key(ip = _L, multicast_ttl = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_IP_MULTICAST_TTL;
-enc_sockopt_key(ip = _L, nodefrag = _Opt, _Dir, _D, raw = _T, _P) ->
- ?ESOCK_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, dgram = _T, _P) ->
- ?ESOCK_OPT_IP_PKTINFO;
-enc_sockopt_key(ip = _L, recvdstaddr = _Opt, _Dir, _D, T, _P) when (T =:= dgram) ->
- ?ESOCK_OPT_IP_RECVDSTADDR;
-enc_sockopt_key(ip = _L, recverr = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_IP_RECVERR;
-enc_sockopt_key(ip = _L, recvif = _Opt, _Dir, _D, T, _P)
- when (T =:= dgram) orelse (T =:= raw) ->
- ?ESOCK_OPT_IP_RECVIF;
-enc_sockopt_key(ip = _L, recvopts = _Opt, _Dir, _D, T, _P) when (T =/= stream) ->
- ?ESOCK_OPT_IP_RECVOPTS;
-enc_sockopt_key(ip = _L, recvorigdstaddr = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_IP_RECVORIGDSTADDR;
-enc_sockopt_key(ip, recvtos = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_IP_RECVTOS;
-enc_sockopt_key(ip = _L, recvttl = _Opt, _Dir, _D, T, _P) when (T =/= stream) ->
- ?ESOCK_OPT_IP_RECVTTL;
-enc_sockopt_key(ip = _L, retopts = _Opt, _Dir, _D, T, _P) when (T =/= stream) ->
- ?ESOCK_OPT_IP_RETOPTS;
-enc_sockopt_key(ip, router_alert = _Opt, _Dir, _D, raw = _T, _P) ->
- ?ESOCK_OPT_IP_ROUTER_ALERT;
-enc_sockopt_key(ip, sendsrcaddr = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_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)...
-enc_sockopt_key(ip, tos = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_IP_TOS;
-enc_sockopt_key(ip = _L, transparent = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_IP_TRANSPARENT;
-enc_sockopt_key(ip, ttl = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_IP_TTL;
-enc_sockopt_key(ip = _L, unblock_source = _Opt, set = _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_IP_UNBLOCK_SOURCE;
-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) ->
- ?ESOCK_OPT_IPV6_ADDRFORM;
-enc_sockopt_key(ipv6, add_membership = _Opt, set = _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_IPV6_ADD_MEMBERSHIP;
-enc_sockopt_key(ipv6 = _L, authhdr = _Opt, _Dir, _D, T, _P)
- when ((T =:= dgram) orelse (T =:= raw)) ->
- ?ESOCK_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) ->
- not_supported({L, Opt});
-enc_sockopt_key(ipv6, drop_membership = _Opt, set = _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_IPV6_DROP_MEMBERSHIP;
-enc_sockopt_key(ipv6 = _L, dstopts = _Opt, _Dir, _D, T, _P)
- when (T =:= dgram) orelse (T =:= raw) ->
- ?ESOCK_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, _Dir, _D, T, _P)
- when (T =:= dgram) orelse (T =:= raw) ->
- ?ESOCK_OPT_IPV6_FLOWINFO;
-enc_sockopt_key(ipv6, hoplimit = _Opt, _Dir, _D, T, _P)
- when (T =:= dgram) orelse (T =:= raw) ->
- ?ESOCK_OPT_IPV6_HOPLIMIT;
-enc_sockopt_key(ipv6 = _L, hopopts = _Opt, _Dir, _D, T, _P)
- when ((T =:= dgram) orelse (T =:= raw)) ->
- ?ESOCK_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) ->
- 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) ->
- ?ESOCK_OPT_IPV6_MTU;
-enc_sockopt_key(ipv6 = _L, mtu_discover = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_IPV6_MTU_DISCOVER;
-enc_sockopt_key(ipv6 = _L, multicast_hops = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_IPV6_MULTICAST_HOPS;
-enc_sockopt_key(ipv6 = _L, multicast_if = _Opt, _Dir, _D, T, _P)
- when (T =:= dgram) orelse (T =:= raw) ->
- ?ESOCK_OPT_IPV6_MULTICAST_IF;
-enc_sockopt_key(ipv6 = _L, multicast_loop = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_IPV6_MULTICAST_LOOP;
-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) ->
- ?ESOCK_OPT_IPV6_RECVERR;
-enc_sockopt_key(ipv6, recvhoplimit = _Opt, _Dir, _D, T, _P)
- when (T =:= dgram) orelse (T =:= raw) ->
- ?ESOCK_OPT_IPV6_RECVHOPLIMIT;
-enc_sockopt_key(ipv6 = _L, Opt, _Dir, _D, T, _P)
- when ((Opt =:= recvpktinfo) orelse (Opt =:= pktinfo)) andalso
- ((T =:= dgram) orelse (T =:= raw)) ->
- ?ESOCK_OPT_IPV6_RECVPKTINFO;
-enc_sockopt_key(ipv6 = _L, recvtclass = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_IPV6_RECVTCLASS;
-enc_sockopt_key(ipv6 = _L, router_alert = _Opt, _Dir, _D, T, _P) when (T =:= raw) ->
- ?ESOCK_OPT_IPV6_ROUTER_ALERT;
-enc_sockopt_key(ipv6 = _L, rthdr = _Opt, _Dir, _D, T, _P)
- when ((T =:= dgram) orelse (T =:= raw)) ->
- ?ESOCK_OPT_IPV6_RTHDR;
-enc_sockopt_key(ipv6 = _L, tclass = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_IPV6_TCLASS;
-enc_sockopt_key(ipv6 = _L, unicast_hops = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_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) ->
- ?ESOCK_OPT_IPV6_V6ONLY;
-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) ->
- ?ESOCK_OPT_TCP_CONGESTION;
-enc_sockopt_key(tcp = _L, cork = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_TCP_CORK;
-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) ->
- ?ESOCK_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) ->
- ?ESOCK_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});
-
-%% UDP socket options
-enc_sockopt_key(udp, cork = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_OPT_UDP_CORK;
-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) ->
- ?ESOCK_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) ->
- 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) ->
- ?ESOCK_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) ->
- ?ESOCK_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) ->
- ?ESOCK_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) ->
- 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) ->
- ?ESOCK_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) ->
- not_supported({L, Opt});
-enc_sockopt_key(sctp = _L, maxseg = _Opt, _Dir, _D, _T, _P) ->
- ?ESOCK_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) ->
- ?ESOCK_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) ->
- ?ESOCK_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, get = _Dir, _D, _T, _P) ->
- not_supported({L, Opt}); % ?ESOCK_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) ->
- unknown({L, UnknownOpt});
-
-%% +++ "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)) orelse
- ((ValueSize =:= int) orelse (ValueSize =:= bool))) ->
- Opt;
-
-enc_sockopt_key(Level, Opt, _Dir, _Domain, _Type, _Protocol) ->
- unknown({Level, Opt}).
-
-
-
-enc_shutdown_how(read) ->
- ?ESOCK_SHUTDOWN_HOW_READ;
-enc_shutdown_how(write) ->
- ?ESOCK_SHUTDOWN_HOW_WRITE;
-enc_shutdown_how(read_write) ->
- ?ESOCK_SHUTDOWN_HOW_READ_WRITE.
-
-
-
-
-%% ===========================================================================
-%%
-%% Misc utility functions
-%%
-%% ===========================================================================
-
--dialyzer({nowarn_function, ensure_ip_msfilter_slist/1}).
-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) ->
- 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 = enc_path(Path),
- 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) ->
- invalid_address(SockAddr).
-
-ensure_open2_opts(M) when is_map(M) ->
- maps:fold(
- fun (Key, Val, Acc) ->
- case Key of
- domain ->
- Acc#{domain => enc_domain(Val)};
- type ->
- Acc#{type => enc_type(Val)};
- protocol ->
- Acc#{protocol => enc_protocol(Val)};
- dup when is_boolean(Val) ->
- Acc#{dup => Val};
- debug when is_boolean(Val) ->
- Acc#{debug => Val};
- _ ->
- Acc
- end
- end, ?OPEN2_OPTS_DEFAULTS, M).
-
-cancel(SockRef, Op, OpRef) ->
- case nif_cancel(SockRef, Op, OpRef) of
- %% The select has already completed
- {error, select_sent} ->
- flush_select_msg(SockRef, OpRef),
- _ = flush_abort_msg(SockRef, OpRef),
- ok;
- Other ->
- _ = flush_abort_msg(SockRef, OpRef),
- Other
- end.
-
-flush_select_msg(SockRef, Ref) ->
- receive
- {?ESOCK_TAG, #socket{ref = SockRef}, select, Ref} ->
- ok
- after 0 ->
- ok
- end.
-
-flush_abort_msg(SockRef, Ref) ->
- receive
- {?ESOCK_TAG, #socket{ref = SockRef}, abort, {Ref, Reason}} ->
- Reason
- after 0 ->
- ok
- 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).
-
-
-deadline(Timeout) ->
- case Timeout of
- nowait -> Timeout;
- infinity -> Timeout;
- _ when is_integer(Timeout), 0 =< Timeout ->
- timestamp() + Timeout;
- _ ->
- invalid_timeout(Timeout)
- end.
-
-timeout(Deadline) ->
- case Deadline of
- nowait -> 0;
- infinity -> infinity;
- _ ->
- Now = timestamp(),
- if
- Now < Deadline -> Deadline - Now;
- true -> 0
- end
- end.
-
-timestamp() ->
- erlang:monotonic_time(milli_seconds).
-
-
--compile({inline, [bincat/2]}).
-bincat(<<>>, <<_/binary>> = B) -> B;
-bincat(<<_/binary>> = A, <<>>) -> A;
-bincat(<<_/binary>> = A, <<_/binary>> = B) ->
- <<A/binary, B/binary>>.
-
-
-%% p(F) ->
-%% p(F, []).
-
-%% p(F, A) ->
-%% p(get(sname), F, A).
-
-%% p(undefined, F, A) ->
-%% p("***", F, A);
-%% p(SName, F, A) ->
-%% TS = formated_timestamp(),
-%% io:format(user,"[~s][~s,~p] " ++ F ++ "~n", [TS, SName, self()|A]),
-%% io:format("[~s][~s,~p] " ++ F ++ "~n", [TS, SName, self()|A]).
-
-
-
-%% ===========================================================================
-%%
-%% Error functions
-%%
-%% ===========================================================================
-
--spec invalid_domain(Domain) -> no_return() when
- Domain :: term().
-invalid_domain(Domain) ->
- error({invalid_domain, Domain}).
-
--spec invalid_type(Type) -> no_return() when
- Type :: term().
-invalid_type(Type) ->
- error({invalid_type, Type}).
-
--spec invalid_protocol(Proto) -> no_return() when
- Proto :: term().
-invalid_protocol(Proto) ->
- error({invalid_protocol, Proto}).
-
--spec invalid_address(SockAddr) -> no_return() when
- SockAddr :: term().
-invalid_address(SockAddr) ->
- error({invalid_address, SockAddr}).
-
--spec invalid_timeout(Timeout) -> no_return() when
- Timeout :: term().
-invalid_timeout(Timeout) ->
- error({invalid_timeout, Timeout}).
-
--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}).
-
-
-%% ===========================================================================
-%%
-%% Below follows the actual NIF-functions.
-%%
-%% ===========================================================================
-
-nif_info() ->
- erlang:nif_error(undef).
-
-nif_info(_SRef) ->
- erlang:nif_error(undef).
-
-nif_command(_Command) ->
- erlang:nif_error(undef).
-
-nif_supports(_Key) ->
- erlang:nif_error(undef).
-
-nif_open(_FD, _Opts) ->
- erlang:nif_error(undef).
-
-nif_open(_Domain, _Type, _Protocol, _Opts) ->
- erlang:nif_error(undef).
-
-nif_bind(_SRef, _SockAddr) ->
- erlang:nif_error(undef).
-
-nif_bind(_SRef, _SockAddrs, _Action) ->
- erlang:nif_error(undef).
-
-nif_connect(_SRef, _SockAddr) ->
- erlang:nif_error(undef).
-
-nif_finalize_connection(_SRef) ->
- erlang:nif_error(undef).
-
-nif_listen(_SRef, _Backlog) ->
- erlang:nif_error(undef).
-
-nif_accept(_SRef, _Ref) ->
- erlang:nif_error(undef).
-
-nif_send(_SockRef, _SendRef, _Data, _Flags) ->
- erlang:nif_error(undef).
-
-nif_sendto(_SRef, _SendRef, _Data, _Dest, _Flags) ->
- erlang:nif_error(undef).
-
-nif_sendmsg(_SRef, _SendRef, _MsgHdr, _Flags) ->
- erlang:nif_error(undef).
-
-nif_recv(_SRef, _RecvRef, _Length, _Flags) ->
- erlang:nif_error(undef).
-
-nif_recvfrom(_SRef, _RecvRef, _Length, _Flags) ->
- erlang:nif_error(undef).
-
-nif_recvmsg(_SRef, _RecvRef, _BufSz, _CtrlSz, _Flags) ->
- erlang:nif_error(undef).
-
-nif_cancel(_SRef, _Op, _Ref) ->
- erlang:nif_error(undef).
-
-nif_close(_SRef) ->
- erlang:nif_error(undef).
-
-nif_shutdown(_SRef, _How) ->
- erlang:nif_error(undef).
-
-nif_finalize_close(_SRef) ->
- erlang:nif_error(undef).
-
-nif_setopt(_Ref, _IsEnc, _Lev, _Key, _Val) ->
- erlang:nif_error(undef).
-
-nif_getopt(_Ref, _IsEnc, _Lev, _Key) ->
- erlang:nif_error(undef).
-
-nif_sockname(_Ref) ->
- erlang:nif_error(undef).
-
-nif_peername(_Ref) ->
- erlang:nif_error(undef).
-
diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile
index 8c205b35a2..d02e954ba9 100644
--- a/lib/kernel/doc/src/Makefile
+++ b/lib/kernel/doc/src/Makefile
@@ -67,9 +67,11 @@ XML_REF3_FILES = application.xml \
pg2.xml \
rpc.xml \
seq_trace.xml \
+ socket.xml \
wrap_log_reader.xml \
user.xml \
- zlib_stub.xml
+ zlib_stub.xml \
+ $(XML_REF3_ESOCK_EFILES)
XML_REF4_FILES = app.xml config.xml
@@ -79,6 +81,7 @@ XML_PART_FILES = part.xml
XML_CHAPTER_FILES = \
notes.xml \
introduction_chapter.xml \
+ socket_usage.xml \
logger_chapter.xml \
logger_cookbook.xml \
eep48_chapter.xml
diff --git a/lib/kernel/doc/src/part.xml b/lib/kernel/doc/src/part.xml
index 083e5c79a0..dd5d955a78 100644
--- a/lib/kernel/doc/src/part.xml
+++ b/lib/kernel/doc/src/part.xml
@@ -32,6 +32,7 @@
<p></p>
</description>
<xi:include href="introduction_chapter.xml"/>
+ <xi:include href="socket_usage.xml"/>
<xi:include href="logger_chapter.xml"/>
<xi:include href="logger_cookbook.xml"/>
<xi:include href="eep48_chapter.xml"/>
diff --git a/lib/kernel/doc/src/ref_man.xml b/lib/kernel/doc/src/ref_man.xml
index 9127157eb5..333cb83bee 100644
--- a/lib/kernel/doc/src/ref_man.xml
+++ b/lib/kernel/doc/src/ref_man.xml
@@ -69,6 +69,7 @@
<xi:include href="pg2.xml"/>
<xi:include href="rpc.xml"/>
<xi:include href="seq_trace.xml"/>
+ <xi:include href="socket.xml"/>
<xi:include href="user.xml"/>
<xi:include href="wrap_log_reader.xml"/>
<xi:include href="zlib_stub.xml"/>
diff --git a/erts/doc/src/socket.xml b/lib/kernel/doc/src/socket.xml
index 53cf9ed0c8..4123d7e784 100644
--- a/erts/doc/src/socket.xml
+++ b/lib/kernel/doc/src/socket.xml
@@ -302,31 +302,29 @@
<name name="int32"/>
</datatype>
<datatype>
- <name name="supports_options_socket"/>
- </datatype>
- <datatype>
- <name name="supports_options_ip"/>
- </datatype>
- <datatype>
- <name name="supports_options_ipv6"/>
- </datatype>
- <datatype>
- <name name="supports_options_tcp"/>
- </datatype>
- <datatype>
- <name name="supports_options_udp"/>
- </datatype>
- <datatype>
- <name name="supports_options_sctp"/>
- </datatype>
- <datatype>
- <name name="supports_options"/>
- </datatype>
- <datatype>
- <name name="supports_send_flags"/>
- </datatype>
- <datatype>
- <name name="supports_recv_flags"/>
+ <name name="errcode"/>
+ <desc>
+ <p>
+ The POSIX error codes are mostly come from the
+ OS level socket interface,
+ but this module may generate some appropriate
+ POSIX codes.
+ </p>
+ <p>
+ The other values come from this module's lower levels
+ and are all fairly fatal internal errors:
+ </p>
+ <taglist>
+ <tag><c>exalloc</c></tag>
+ <item>Memory allocation failed</item>
+ <tag><c>exmonitor</c></tag>
+ <item>Failed to set a monitor on a process</item>
+ <tag><c>exselect</c></tag>
+ <item>Select operation failed</item>
+ <tag><c>exself</c></tag>
+ <item>Failed to get current process</item>
+ </taglist>
+ </desc>
</datatype>
</datatypes>
@@ -956,36 +954,178 @@
</func>
<func>
+ <name since="OTP 22.0">
+ supports() -> Supports
+ </name>
+ <name since="OTP 22.0">
+ supports(Key1 :: options) -> SupportsOptions
+ </name>
+ <name since="OTP 22.0">
+ supports(Key1 :: send_flags) -> SupportsSendFlags
+ </name>
+ <name since="OTP 22.0">
+ supports(Key1 :: recv_flags) -> SupportsRecvFlags
+ </name>
+ <name since="OTP 22.0">
+ supports(Key1 :: options, Key2 :: socket) -> SupportsOptionsSocket
+ </name>
+ <name since="OTP 22.0">
+ supports(Key1 :: options, Key2 :: ip) -> SupportsOptionsIP
+ </name>
+ <name since="OTP 22.0">
+ supports(Key1 :: options, Key2 :: ipv6) -> SupportsOptionsIPv6
+ </name>
+ <name since="OTP 22.0">
+ supports(Key1 :: options, Key2 :: tcp) -> SupportsOptionsTCP
+ </name>
+ <name since="OTP 22.0">
+ supports(Key1 :: options, Key2 :: udp) -> SupportsOptionsUDP
+ </name>
+ <name since="OTP 22.0">
+ supports(Key1 :: options, Key2 :: sctp) -> SupportsOptionsSCTP
+ </name>
+ <!--
<name name="supports" arity="0" since="OTP 22.0"/>
- <name name="supports" arity="1" clause_i="1" since="OTP 22.0"/>
- <name name="supports" arity="1" clause_i="2" since="OTP 22.0"/>
- <name name="supports" arity="1" clause_i="3" since="OTP 22.0"/>
- <name name="supports" arity="1" clause_i="4" since="OTP 22.0"/>
- <name name="supports" arity="1" clause_i="5" since="OTP 22.3"/>
- <name name="supports" arity="1" clause_i="6" since="OTP 22.3"/>
- <name name="supports" arity="1" clause_i="7" since="OTP @OTP-16432@"/>
- <name name="supports" arity="1" clause_i="8" since="OTP 22.0"/>
- <name name="supports" arity="2" clause_i="1" since="OTP 22.0"/>
- <name name="supports" arity="2" clause_i="2" since="OTP 22.0"/>
- <name name="supports" arity="2" clause_i="3" since="OTP 22.0"/>
- <name name="supports" arity="2" clause_i="4" since="OTP 22.0"/>
- <name name="supports" arity="2" clause_i="5" since="OTP 22.0"/>
- <name name="supports" arity="2" clause_i="6" since="OTP 22.0"/>
- <name name="supports" arity="2" clause_i="7" since="OTP 22.3"/>
- <name name="supports" arity="2" clause_i="8" since="OTP 22.3"/>
- <name name="supports" arity="2" clause_i="9" since="OTP 22.0"/>
- <name name="supports" arity="3" clause_i="1" since="OTP 22.0"/>
- <name name="supports" arity="3" clause_i="2" since="OTP 22.0"/>
- <name name="supports" arity="3" clause_i="3" since="OTP 22.0"/>
- <name name="supports" arity="3" clause_i="4" since="OTP 22.0"/>
- <name name="supports" arity="3" clause_i="5" since="OTP 22.0"/>
- <name name="supports" arity="3" clause_i="6" since="OTP 22.0"/>
- <name name="supports" arity="3" clause_i="7" since="OTP 22.0"/>
+ <name name="supports" arity="1" since="OTP 22.0"/>
+ <name name="supports" arity="2" since="OTP 22.0"/>
+ -->
+ <fsummary>Report info about what the platform supports.</fsummary>
+ <type>
+ <v>
+ Supports :: [{Feature,&nbsp;boolean()}
+ &nbsp;&nbsp;|&nbsp;{send_flags,&nbsp;SupportsSendFlags}
+ &nbsp;&nbsp;|&nbsp;{recv_flags,&nbsp;SupportsRecvFlags}
+ &nbsp;&nbsp;|&nbsp;{options,&nbsp;SupportsOptions}]
+ </v>
+ <v>Feature :: sctp | ipv6 | local | netns</v>
+ <v>
+ SupportsSendFlags ::
+ [{<seetype marker="#send_flag">send_flag()</seetype>,&nbsp;boolean()}]
+ </v>
+ <v>
+ SupportsRecvFlags ::
+ [{<seetype marker="#recv_flag">recv_flag()</seetype>,&nbsp;boolean()}]
+ </v>
+ <v>
+ SupportsOptions ::
+ [{socket,&nbsp;SupportsOptionsSocket}
+ &nbsp;&nbsp;|&nbsp;{ip,&nbsp;SupportsOptionsIP}
+ &nbsp;&nbsp;|&nbsp;{ipv6,&nbsp;SupportsOptionsIPv6}
+ &nbsp;&nbsp;|&nbsp;{tcp,&nbsp;SupportsOptionsTCP}
+ &nbsp;&nbsp;|&nbsp;{udp,&nbsp;SupportsOptionsUDP}
+ &nbsp;&nbsp;|&nbsp;{sctp,&nbsp;SupportsOptionsSCTP}]
+ </v>
+ <v>
+ SupportsOptionsSocket ::
+ [{<seetype marker="#socket_option">socket_option()</seetype>,&nbsp;boolean()}]
+ </v>
+ <v>
+ SupportsOptionsIP ::
+ [{<seetype marker="#ip_socket_option">ip_socket_option()</seetype>,&nbsp;boolean()}]
+ </v>
+ <v>
+ SupportsOptionsIPv6 ::
+ [{<seetype marker="#ipv6_socket_option">ipv6_socket_option()</seetype>,&nbsp;boolean()}]
+ </v>
+ <v>
+ SupportsOptionsTCP ::
+ [{<seetype marker="#tcp_socket_option">tcp_socket_option()</seetype>,&nbsp;boolean()}]
+ </v>
+ <v>
+ SupportsOptionsUDP ::
+ [{<seetype marker="#udp_socket_option">udp_socket_option()</seetype>,&nbsp;boolean()}]
+ </v>
+ <v>
+ SupportsOptionsSCTP ::
+ [{<seetype marker="#sctp_socket_option">sctp_socket_option()</seetype>,&nbsp;boolean()}]
+ </v>
+ </type>
+ <desc>
+ <p>
+ This function retreives information about what the
+ platform supports, such as if SCTP is supported,
+ or which socket options are supported.
+ </p>
+ <p>
+ For keys other than the known the empty list is returned,
+ Note that in a future version or on a different platform
+ there might be more supported items.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name since="OTP 23.0">
+ is_supported(Key1 :: sctp | ipv6 | local | netns) -> boolean()
+ </name>
+ <name since="OTP 23.0">
+ is_supported(Key1 :: send_flags, Key2 :: SendFlag) -> boolean()
+ </name>
+ <name since="OTP 23.0">
+ is_supported(Key1 :: recv_flags, Key2 :: RecvFlag) -> boolean()
+ </name>
+ <name since="OTP 23.0">
+ is_supported(Key1 :: options, Key2 :: socket, Key3 :: SocketOption) ->
+ boolean()
+ </name>
+ <name since="OTP 23.0">
+ is_supported(Key1 :: options, Key2 :: ip, Key3 :: IPSocketOption) ->
+ boolean()
+ </name>
+ <name since="OTP 23.0">
+ is_supported(Key1 :: options, Key2 :: ipv6, Key3 :: IPv6SocketOption) ->
+ boolean()
+ </name>
+ <name since="OTP 23.0">
+ is_supported(Key1 :: options, Key2 :: tcp, Key3 :: TCPSocketOption) ->
+ boolean()
+ </name>
+ <name since="OTP 23.0">
+ is_supported(Key1 :: options, Key2 :: udp, Key3 :: UDPSocketOption) ->
+ boolean()
+ </name>
+ <name since="OTP 23.0">
+ is_supported(Key1 :: options, Key2 :: sctp, Key3 :: SCTPSocketOption) ->
+ boolean()
+ </name>
<fsummary>Report info about what the platform supports.</fsummary>
+ <type>
+ <v>
+ SocketOption ::
+ <seetype marker="#socket_option">socket_option()</seetype>
+ </v>
+ <v>
+ IPSocketOption ::
+ <seetype marker="#ip_socket_option">ip_socket_option()</seetype>
+ </v>
+ <v>
+ IPv6SocketOption ::
+ <seetype marker="#ipv6_socket_option">ipv6_socket_option()</seetype>
+ </v>
+ <v>
+ TCPSocketOption ::
+ <seetype marker="#tcp_socket_option">tcp_socket_option()</seetype>
+ </v>
+ <v>
+ UDPSocketOption ::
+ <seetype marker="#udp_socket_option">udp_socket_option()</seetype>
+ </v>
+ <v>
+ SCTPSocketOption ::
+ <seetype marker="#sctp_socket_option">sctp_socket_option()</seetype>
+ </v>
+ </type>
<desc>
- <p>This function intends to retreive information about what the
- platform supports. Such as if SCTP is supported. Or which socket
- options are supported. </p>
+ <p>
+ This function retreives information about what the
+ platform supports, such as if SCTP is supported,
+ or which socket options are supported.
+ </p>
+ <p>
+ For keys other than the known <c>false</c> is returned.
+ Note that in a future version or on a different platform
+ there might be more supported items.
+ </p>
</desc>
</func>
diff --git a/erts/doc/src/socket_usage.xml b/lib/kernel/doc/src/socket_usage.xml
index e25c995098..e25c995098 100644
--- a/erts/doc/src/socket_usage.xml
+++ b/lib/kernel/doc/src/socket_usage.xml
diff --git a/lib/kernel/doc/src/specs.xml b/lib/kernel/doc/src/specs.xml
index 00f6f04218..990bc85782 100644
--- a/lib/kernel/doc/src/specs.xml
+++ b/lib/kernel/doc/src/specs.xml
@@ -35,6 +35,7 @@
<xi:include href="../specs/specs_pg2.xml"/>
<xi:include href="../specs/specs_rpc.xml"/>
<xi:include href="../specs/specs_seq_trace.xml"/>
+ <xi:include href="../specs/specs_socket.xml"/>
<xi:include href="../specs/specs_user.xml"/>
<xi:include href="../specs/specs_wrap_log_reader.xml"/>
<xi:include href="../specs/specs_zlib_stub.xml"/>
diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile
index 07f4f2304c..6c75bcffee 100644
--- a/lib/kernel/src/Makefile
+++ b/lib/kernel/src/Makefile
@@ -135,6 +135,7 @@ MODULES = \
ram_file \
rpc \
seq_trace \
+ socket \
standard_error \
user \
user_drv \
diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl
index 0c98f62cdf..b44144d88a 100644
--- a/lib/kernel/src/gen_tcp.erl
+++ b/lib/kernel/src/gen_tcp.erl
@@ -130,7 +130,7 @@
{netns, file:filename_all()} |
{bind_to_device, binary()} |
option().
--type socket() :: port().
+-type socket() :: inet:socket().
-export_type([option/0, option_name/0, connect_option/0, listen_option/0,
socket/0, pktoptions_value/0]).
diff --git a/lib/kernel/src/gen_tcp_socket.erl b/lib/kernel/src/gen_tcp_socket.erl
index fe58cc0cba..4cf804a414 100644
--- a/lib/kernel/src/gen_tcp_socket.erl
+++ b/lib/kernel/src/gen_tcp_socket.erl
@@ -291,10 +291,10 @@ send(?module_socket(Server, Socket), Data) ->
Result = socket_send(Socket, Data, SendTimeout),
send_result(Server, Meta, Result)
end;
- {ok, _Meta} ->
+ {ok, _BadMeta} ->
exit(badarg);
{error, _} = Error ->
- ?badarg_exit(Error)
+ Error
end.
%%
send_result(Server, Meta, Result) ->
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index 8b944a1049..6ae5351652 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -128,7 +128,8 @@
'etimedout' |
'ewouldblock' |
'exbadport' | 'exbadseq' | file:posix().
--type socket() :: port().
+-type module_socket() :: {'$inet', Handler :: module(), Handle :: term()}.
+-type socket() :: port() | module_socket().
-type socket_setopt() ::
gen_sctp:option() | gen_tcp:option() | gen_udp:option().
@@ -1433,7 +1434,7 @@ gethostbyaddr_tm_native(Addr, Timer, Opts) ->
Family :: address_family(),
Type :: socket_type(),
Module :: atom()) ->
- {'ok', socket()} | {'error', posix()}.
+ {'ok', port()} | {'error', posix()}.
open(FdO, Addr, Port, Opts, Protocol, Family, Type, Module)
when is_integer(FdO), FdO < 0;
diff --git a/lib/kernel/src/inet6_udp.erl b/lib/kernel/src/inet6_udp.erl
index 7ebdb3d301..b8c33e91ec 100644
--- a/lib/kernel/src/inet6_udp.erl
+++ b/lib/kernel/src/inet6_udp.erl
@@ -44,10 +44,10 @@ getaddr(Address, Timer) -> inet:getaddr(Address, ?FAMILY, Timer).
%% inet_udp special this side addresses
translate_ip(IP) -> inet:translate_ip(IP, ?FAMILY).
--spec open(_) -> {ok, inet:socket()} | {error, atom()}.
+-spec open(_) -> {ok, port()} | {error, atom()}.
open(Port) -> open(Port, []).
--spec open(_, _) -> {ok, inet:socket()} | {error, atom()}.
+-spec open(_, _) -> {ok, port()} | {error, atom()}.
open(Port, Opts) ->
case inet:udp_options(
[{port,Port} | Opts],
@@ -91,7 +91,7 @@ recv(S, Len) ->
recv(S, Len, Time) ->
prim_inet:recvfrom(S, Len, Time).
--spec close(inet:socket()) -> ok.
+-spec close(port()) -> ok.
close(S) ->
inet:udp_close(S).
diff --git a/lib/kernel/src/inet_udp.erl b/lib/kernel/src/inet_udp.erl
index ace095f30c..00ebd4bb1d 100644
--- a/lib/kernel/src/inet_udp.erl
+++ b/lib/kernel/src/inet_udp.erl
@@ -45,10 +45,10 @@ getaddr(Address, Timer) -> inet:getaddr(Address, ?FAMILY, Timer).
%% inet_udp special this side addresses
translate_ip(IP) -> inet:translate_ip(IP, ?FAMILY).
--spec open(_) -> {ok, inet:socket()} | {error, atom()}.
+-spec open(_) -> {ok, port()} | {error, atom()}.
open(Port) -> open(Port, []).
--spec open(_, _) -> {ok, inet:socket()} | {error, atom()}.
+-spec open(_, _) -> {ok, port()} | {error, atom()}.
open(Port, Opts) ->
case inet:udp_options(
[{port,Port}, {recbuf, ?RECBUF} | Opts],
@@ -92,7 +92,7 @@ recv(S, Len) ->
recv(S, Len, Time) ->
prim_inet:recvfrom(S, Len, Time).
--spec close(inet:socket()) -> ok.
+-spec close(port()) -> ok.
close(S) ->
inet:udp_close(S).
diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src
index 48c77c49a9..e58151f295 100644
--- a/lib/kernel/src/kernel.app.src
+++ b/lib/kernel/src/kernel.app.src
@@ -115,6 +115,7 @@
raw_file_io_list,
raw_file_io_raw,
seq_trace,
+ socket,
standard_error,
wrap_log_reader]},
{registered, [application_controller,
diff --git a/lib/kernel/src/net.erl b/lib/kernel/src/net.erl
index aceb903bb6..905114ab7c 100644
--- a/lib/kernel/src/net.erl
+++ b/lib/kernel/src/net.erl
@@ -152,7 +152,6 @@ relay(X) -> slave:relay(X).
info() ->
prim_net:info().
-else.
--dialyzer({nowarn_function, info/0}).
info() ->
erlang:error(notsup).
-endif.
@@ -164,7 +163,6 @@ info() ->
command(Cmd) ->
prim_net:command(Cmd).
-else.
--dialyzer({nowarn_function, command/1}).
command(_Cmd) ->
erlang:error(notsup).
-endif.
@@ -191,7 +189,6 @@ command(_Cmd) ->
gethostname() ->
prim_net:gethostname().
-else.
--dialyzer({nowarn_function, gethostname/0}).
gethostname() ->
erlang:error(notsup).
-endif.
@@ -208,6 +205,8 @@ gethostname() ->
Info :: name_info(),
Reason :: term().
+-dialyzer({no_return, getnameinfo/1}).
+
getnameinfo(SockAddr) ->
getnameinfo(SockAddr, undefined).
@@ -226,25 +225,18 @@ getnameinfo(SockAddr) ->
-endif.
-ifdef(USE_ESOCK).
-getnameinfo(SockAddr, [] = _Flags) ->
- getnameinfo(SockAddr, undefined);
-getnameinfo(#{family := Fam, addr := _Addr} = SockAddr, Flags)
- when ((Fam =:= inet) orelse (Fam =:= inet6)) andalso
- (is_list(Flags) orelse (Flags =:= undefined)) ->
- prim_net:getnameinfo(socket:ensure_sockaddr(SockAddr), Flags);
-getnameinfo(#{family := Fam, path := _Path} = SockAddr, Flags)
- when (Fam =:= local) andalso (is_list(Flags) orelse (Flags =:= undefined)) ->
+getnameinfo(SockAddr, Flags)
+ when is_map(SockAddr), is_list(Flags);
+ is_map(SockAddr), Flags =:= undefined ->
prim_net:getnameinfo(SockAddr, Flags).
+
-else.
--dialyzer({nowarn_function, getnameinfo/2}).
-getnameinfo(SockAddr, [] = _Flags) ->
- getnameinfo(SockAddr, undefined);
-getnameinfo(#{family := Fam, addr := _Addr} = _SockAddr, Flags)
- when ((Fam =:= inet) orelse (Fam =:= inet6)) andalso
- (is_list(Flags) orelse (Flags =:= undefined)) ->
- erlang:error(notsup);
-getnameinfo(#{family := Fam, path := _Path} = _SockAddr, Flags)
- when (Fam =:= local) andalso (is_list(Flags) orelse (Flags =:= undefined)) ->
+
+-dialyzer({no_return, getnameinfo/2}).
+
+getnameinfo(SockAddr, Flags)
+ when is_map(SockAddr), is_list(Flags);
+ is_map(SockAddr), Flags =:= undefined ->
erlang:error(notsup).
-endif.
@@ -260,6 +252,8 @@ getnameinfo(#{family := Fam, path := _Path} = _SockAddr, Flags)
Info :: [address_info()],
Reason :: term().
+-dialyzer({no_return, getaddrinfo/1}).
+
getaddrinfo(Host) when is_list(Host) ->
getaddrinfo(Host, undefined).
@@ -285,7 +279,6 @@ getaddrinfo(Host, Service)
(not ((Service =:= undefined) andalso (Host =:= undefined))) ->
prim_net:getaddrinfo(Host, Service).
-else.
--dialyzer({nowarn_function, getaddrinfo/2}).
getaddrinfo(Host, Service)
when (is_list(Host) orelse (Host =:= undefined)) andalso
(is_list(Service) orelse (Service =:= undefined)) andalso
@@ -332,7 +325,6 @@ getifaddrs(Filter) when is_function(Filter, 1) ->
getifaddrs(Namespace) when is_list(Namespace) ->
prim_net:getifaddrs(#{netns => Namespace}).
-else.
--dialyzer({nowarn_function, getifaddrs/1}).
getifaddrs(Filter) when is_atom(Filter) orelse
is_map(Filter) orelse
is_function(Filter) ->
@@ -348,6 +340,8 @@ getifaddrs(Namespace) when is_list(Namespace) ->
IfAddrs :: [ifaddrs()],
Reason :: term().
+-dialyzer({no_return, getifaddrs/2}).
+
getifaddrs(Filter, Namespace)
when (is_atom(Filter) orelse is_map(Filter)) andalso is_list(Namespace) ->
do_getifaddrs(getifaddrs_filter_map(Filter),
@@ -356,6 +350,8 @@ getifaddrs(Filter, Namespace)
when is_function(Filter, 1) andalso is_list(Namespace) ->
do_getifaddrs(Filter, fun() -> getifaddrs(Namespace) end).
+-dialyzer({no_return, do_getifaddrs/2}).
+
do_getifaddrs(Filter, GetIfAddrs) ->
case GetIfAddrs() of
{ok, IfAddrs0} when is_function(Filter) ->
@@ -395,6 +391,8 @@ getifaddrs_filter_map_inet6() ->
getifaddrs_filter_map_packet() ->
#{family => packet, flags => any}.
+-compile({nowarn_unused_function, getifaddrs_filter/2}).
+
getifaddrs_filter(#{family := FFamily, flags := FFlags},
#{addr := #{family := Family}, flags := Flags} = _Entry)
when (FFamily =:= default) andalso
@@ -419,6 +417,8 @@ getifaddrs_filter(#{family := FFamily, flags := FFlags},
getifaddrs_filter(_Filter, _Entry) ->
false.
+-compile({nowarn_unused_function, getifaddrs_filter_flags/2}).
+
getifaddrs_filter_flags(any, _Flags) ->
true;
getifaddrs_filter_flags(FilterFlags, Flags) ->
@@ -442,7 +442,6 @@ getifaddrs_filter_flags(FilterFlags, Flags) ->
if_name2index(If) when is_list(If) ->
prim_net:if_name2index(If).
-else.
--dialyzer({nowarn_function, if_name2index/1}).
if_name2index(If) when is_list(If) ->
erlang:error(notsup).
-endif.
@@ -465,7 +464,6 @@ if_name2index(If) when is_list(If) ->
if_index2name(Idx) when is_integer(Idx) ->
prim_net:if_index2name(Idx).
-else.
--dialyzer({nowarn_function, if_index2name/1}).
if_index2name(Idx) when is_integer(Idx) ->
erlang:error(notsup).
-endif.
@@ -488,7 +486,6 @@ if_index2name(Idx) when is_integer(Idx) ->
if_names() ->
prim_net:if_names().
-else.
--dialyzer({nowarn_function, if_names/0}).
if_names() ->
erlang:error(notsup).
-endif.
diff --git a/lib/kernel/src/socket.erl b/lib/kernel/src/socket.erl
new file mode 100644
index 0000000000..3864a88a4f
--- /dev/null
+++ b/lib/kernel/src/socket.erl
@@ -0,0 +1,2563 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2020. 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).
+
+-compile({no_auto_import,[error/1]}).
+
+%% Administrative and "global" utility functions
+-export([
+ number_of/0,
+ which_sockets/0, which_sockets/1,
+
+ debug/1, socket_debug/1,
+ info/0, info/1,
+ supports/0, supports/1, supports/2,
+ is_supported/1, is_supported/2, is_supported/3
+ ]).
+
+-export([
+ open/1, open/2, open/3, open/4,
+ bind/2, bind/3,
+ connect/1, connect/2, connect/3,
+ listen/1, listen/2,
+ accept/1, accept/2,
+
+ send/2, send/3, send/4,
+ sendto/3, sendto/4, sendto/5,
+ sendmsg/2, sendmsg/3, sendmsg/4,
+
+ recv/1, recv/2, recv/3, recv/4,
+ recvfrom/1, recvfrom/2, recvfrom/3, recvfrom/4,
+ recvmsg/1, recvmsg/2, recvmsg/3, recvmsg/5,
+
+ close/1,
+ shutdown/2,
+
+ setopt/4,
+ getopt/3,
+
+ sockname/1,
+ peername/1,
+
+ cancel/2
+ ]).
+
+-export_type([
+ select_tag/0,
+ select_ref/0,
+ select_info/0,
+
+ socket_counters/0,
+ socket_counter/0,
+ socket_info/0,
+
+ domain/0,
+ type/0,
+ protocol/0,
+ socket/0,
+
+ port_number/0,
+ ip_address/0,
+ ip4_address/0,
+ ip6_address/0,
+ sockaddr/0,
+ sockaddr_in4/0,
+ sockaddr_in6/0,
+ sockaddr_un/0,
+ sockaddr_ll/0,
+
+ send_flags/0,
+ send_flag/0,
+
+ recv_flags/0,
+ recv_flag/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,
+ raw_socket_option/0,
+
+ timeval/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,
+ ipv6_pktinfo/0,
+ in6_flow_info/0,
+ in6_scope_id/0,
+ sctp_assoc_id/0,
+ sctp_sndrcvinfo/0,
+ sctp_event_subscribe/0,
+ sctp_assocparams/0,
+ sctp_initmsg/0,
+ sctp_rtoinfo/0,
+
+
+ msghdr_flag/0,
+ msghdr_flags/0,
+ msghdr/0,
+ cmsghdr_level/0,
+ cmsghdr_type/0,
+ %% cmsghdr_data/0,
+ cmsghdr_recv/0, cmsghdr_send/0,
+
+ ee_origin/0,
+ icmp_dest_unreach/0,
+ icmpv6_dest_unreach/0,
+ extended_err/0,
+
+ uint8/0,
+ uint16/0,
+ uint20/0,
+ uint32/0,
+ int32/0
+ ]).
+
+%% Also in prim_socket
+-define(REGISTRY, socket_registry).
+
+
+-type socket_counters() :: [{socket_counter(), non_neg_integer()}].
+-type socket_counter() :: read_byte | read_fails | read_pkg | read_pkg_max |
+ read_tries | read_waits |
+ write_byte | write_fails | write_pkg | write_pkg_max |
+ write_tries | write_waits |
+ acc_success | acc_fails | acc_tries | acc_waits.
+-type socket_info() :: #{domain := domain(),
+ type := type(),
+ protocol := protocol(),
+ ctrl := pid(),
+ ctype := normal | fromfd | {fromfd, integer()},
+ counters := socket_counters(),
+ num_readers := non_neg_integer(),
+ num_writers := non_neg_integer(),
+ num_acceptors := non_neg_integer(),
+ writable := boolean(),
+ readable := boolean()}.
+
+-type uint8() :: 0..16#FF.
+-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.
+-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:
+%% 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.
+
+-type ip_address() :: ip4_address() | ip6_address().
+
+-type ip4_address() :: {0..255, 0..255, 0..255, 0..255}.
+
+-type in6_flow_info() :: uint20().
+-type in6_scope_id() :: uint32().
+
+-type ip6_address() ::
+ {0..65535,
+ 0..65535,
+ 0..65535,
+ 0..65535,
+ 0..65535,
+ 0..65535,
+ 0..65535,
+ 0..65535}.
+
+-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() :: lowdelay |
+ throughput |
+ reliability |
+ mincost |
+ integer().
+
+%% 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 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.
+
+%% 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()}.
+
+-type ipv6_pmtudisc() :: ip_pmtudisc().
+
+-type ipv6_pktinfo() :: #{
+ addr := ip6_address(),
+ ifindex := integer()
+ }.
+
+
+-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(),
+ send_failure := boolean(),
+ peer_error := boolean(),
+ shutdown := boolean(),
+ partial_delivery := boolean(),
+ adaptation_layer := boolean(),
+ authentication := boolean(),
+ sender_dry := boolean()}.
+
+-type sctp_assocparams() :: #{assoc_id := sctp_assoc_id(),
+ 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 := sctp_assoc_id(),
+ initial := uint32(),
+ max := uint32(),
+ min := uint32()}.
+
+-type sockaddr_un() :: #{family := local,
+ path := binary() | string()}.
+-type sockaddr_in4() :: #{family := inet,
+ port := port_number(),
+ %% The 'broadcast' here is the "limited broadcast"
+ addr := any | broadcast | 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_ll() :: #{family := packet,
+ protocol := non_neg_integer(),
+ ifindex := integer(),
+ pkttype := packet_type(),
+ hatype := non_neg_integer(),
+ addr := binary()}.
+-type packet_type() :: host | broadcast | multicast | otherhost |
+ outgoing | loopback | user | kernel | fastroute |
+ non_neg_integer().
+-type sockaddr() :: sockaddr_in4() |
+ sockaddr_in6() |
+ sockaddr_un() |
+ sockaddr_ll().
+
+%% otp - This option is internal to our (OTP) implementation.
+%% socket - The socket layer (SOL_SOCKET).
+%% 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".
+%% Its up to the caller to make sure this is correct!
+-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?
+%% Do we really need a sndbuf?
+
+-type otp_socket_option() :: debug |
+ iow |
+ controlling_process |
+ rcvbuf | % sndbuf |
+ rcvctrlbuf |
+ sndctrlbuf |
+ meta |
+ fd.
+%% 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 |
+ domain |
+ dontroute |
+ error |
+ keepalive |
+ linger |
+ mark |
+ oobinline |
+ passcred |
+ peek_off |
+ peercred |
+ priority |
+ protocol |
+ rcvbuf |
+ rcvbufforce |
+ rcvlowat |
+ rcvtimeo |
+ reuseaddr |
+ reuseport |
+ rxq_ovfl |
+ setfib |
+ sndbuf |
+ sndbufforce |
+ sndlowat |
+ sndtimeo |
+ timestamp |
+ type.
+
+%% Read-only options:
+%% mtu
+%%
+%% Options only valid on FreeBSD?:
+%% dontfrag
+%% Options only valid for RAW sockets:
+%% nodefrag (linux only?)
+-type ip_socket_option() :: add_membership |
+ add_source_membership |
+ block_source |
+ dontfrag |
+ 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() ::
+ addrform |
+ add_membership |
+ authhdr |
+ auth_level |
+ checksum |
+ drop_membership |
+ dstopts |
+ esp_trans_level |
+ esp_network_level |
+ faith |
+ flowinfo |
+ hopopts |
+ ipcomp_level |
+ join_group |
+ leave_group |
+ mtu |
+ mtu_discover |
+ multicast_hops |
+ multicast_if |
+ multicast_loop |
+ portrange |
+ pktoptions |
+ recverr |
+ recvhoplimit | hoplimit |
+ recvpktinfo | pktinfo |
+ recvtclass |
+ router_alert |
+ rthdr |
+ tclass |
+ unicast_hops |
+ use_min_mtu |
+ v6only.
+
+-type tcp_socket_option() :: congestion |
+ cork |
+ info |
+ keepcnt |
+ keepidle |
+ keepintvl |
+ maxseg |
+ md5sig |
+ nodelay |
+ noopt |
+ nopush |
+ syncnt |
+ user_timeout.
+
+-type udp_socket_option() :: cork.
+
+-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 raw_socket_option() :: filter.
+
+%% -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() |
+%% raw_socket_option() |
+%% plain_socket_option().
+
+%% The names of these macros match the names of corresponding
+%%C functions in the NIF code, so a search will match both
+%%
+-define(socket_tag, '$socket').
+%%
+%% Our socket abstract data type
+-define(mk_socket(Ref), {?socket_tag, (Ref)}).
+%%
+%% Messages sent from the nif-code to erlang processes:
+-define(mk_socket_msg(Socket, Tag, Info), {?socket_tag, (Socket), (Tag), (Info)}).
+
+-opaque socket() :: ?mk_socket(reference()).
+
+-type send_flags() :: [send_flag()].
+-type send_flag() :: confirm |
+ dontroute |
+ eor |
+ more |
+ nosignal |
+ oob.
+
+%% Note that not all of these flags are useful for every recv function!
+%%
+-type recv_flags() :: [recv_flag()].
+-type recv_flag() :: cmsg_cloexec |
+ errqueue |
+ oob |
+ peek |
+ trunc.
+
+-type shutdown_how() :: read | write | read_write.
+
+-type msghdr_flag() :: ctrunc | eor | errqueue | oob | trunc.
+-type msghdr_flags() :: [msghdr_flag()].
+-type msghdr() :: #{
+ %% *Optional* target address
+ %% 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_recv()] | [cmsghdr_send()],
+
+ %% Only valid with recvmsg
+ 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 cmsghdr_level() :: socket | ip | ipv6 | integer().
+-type cmsghdr_type() :: credentials |
+ hoplevel |
+ origdstaddr |
+ pktinfo |
+ recvtos |
+ rights |
+ timestamp |
+ tos |
+ ttl |
+ integer().
+-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 := recvtos, data := ip_tos()} |
+ #{level := ip, type := ttl, data := integer()} |
+ #{level := ip, type := recvttl, data := integer()} |
+ #{level := ip, type := pktinfo, data := ip_pktinfo()} |
+ #{level := ip, type := origdstaddr, data := sockaddr_in4()} |
+ #{level := ip, type := recverr, data := extended_err() | binary()} |
+ #{level := ip, type := integer(), data := binary()} |
+ #{level := ipv6, type := hoplevel, data := integer()} |
+ #{level := ipv6, type := pktinfo, data := ipv6_pktinfo()} |
+ #{level := ipv6, type := recverr, data := extended_err() | binary()} |
+ #{level := ipv6, type := tclass, data := integer()} |
+ #{level := ipv6, type := integer(), data := binary()} |
+ #{level := integer(), type := integer(), data := binary()}.
+-type cmsghdr_send() ::
+ #{level := socket, type := timestamp, data := binary()} |
+ #{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() | binary()} |
+ #{level := ip, type := ttl, data := integer() | binary()} |
+ #{level := ip, type := integer(), data := binary()} |
+ #{level := ipv6, type := tclass, data := integer()} |
+ #{level := ipv6, type := integer(), data := binary()} |
+ #{level := udp, type := integer(), data := binary()} |
+ #{level := integer(), type := integer(), data := binary()}.
+
+-type ee_origin() :: none | local | icmp | icmp6 | uint8().
+-type icmp_dest_unreach() :: net_unreach | host_unreach | port_unreach | frag_needed |
+ net_unknown | host_unknown | uint8().
+-type icmpv6_dest_unreach() :: noroute | adm_prohibited | not_neighbour | addr_unreach |
+ port_unreach | policy_fail | reject_route | uint8().
+-type extended_err() ::
+ #{error := term(),
+ origin := icmp,
+ type := dest_unreach,
+ code := icmp_dest_unreach(),
+ info := uint32(),
+ data := uint32(),
+ offender := undefined | sockaddr()} |
+ #{error := term(),
+ origin := icmp,
+ type := time_exceeded | uint8(),
+ code := uint8(),
+ info := uint32(),
+ data := uint32(),
+ offender := undefined | sockaddr()} |
+ #{error := term(),
+ origin := icmp6,
+ type := dest_unreach,
+ code := icmpv6_dest_unreach(),
+ info := uint32(),
+ data := uint32(),
+ offender := undefined | sockaddr()} |
+ #{error := term(),
+ origin := icmp6,
+ type := pkt_toobig | time_exceeded | uint8(),
+ code := uint8(),
+ info := uint32(),
+ data := uint32(),
+ offender := undefined | sockaddr()} |
+ #{error := term(),
+ origin := ee_origin(),
+ type := uint8(),
+ code := uint8(),
+ info := uint32(),
+ data := uint32(),
+ offender := undefined | sockaddr()}.
+
+-type errcode() ::
+ inet:posix() | % closed | timeout | not_owner |
+ exalloc | exmonitor | exselect | exself.
+
+%% ===========================================================================
+%%
+%% Interface term formats
+%%
+
+-opaque select_tag() :: atom().
+-opaque select_ref() :: reference().
+
+-type select_info() :: {select_info, select_tag(), select_ref()}.
+
+-define(SELECT_INFO(T, R), {select_info, T, R}).
+-define(SELECT(T, R), {select, ?SELECT_INFO(T, R)}).
+
+
+%% ===========================================================================
+%%
+%% Defaults
+%%
+
+-define(ESOCK_LISTEN_BACKLOG_DEFAULT, 5).
+
+-define(ESOCK_ACCEPT_TIMEOUT_DEFAULT, infinity).
+
+-define(ESOCK_SEND_FLAGS_DEFAULT, []).
+-define(ESOCK_SEND_TIMEOUT_DEFAULT, infinity).
+-define(ESOCK_SENDTO_FLAGS_DEFAULT, []).
+-define(ESOCK_SENDTO_TIMEOUT_DEFAULT, ?ESOCK_SEND_TIMEOUT_DEFAULT).
+-define(ESOCK_SENDMSG_FLAGS_DEFAULT, []).
+-define(ESOCK_SENDMSG_TIMEOUT_DEFAULT, ?ESOCK_SEND_TIMEOUT_DEFAULT).
+
+-define(ESOCK_RECV_FLAGS_DEFAULT, []).
+-define(ESOCK_RECV_TIMEOUT_DEFAULT, infinity).
+
+
+%% ===========================================================================
+%%
+%% Administrative and utility API
+%%
+%% ===========================================================================
+
+%% *** number_of ***
+%%
+%% Interface function to the socket registry
+%% returns the number of existing (and "alive") sockets.
+%%
+-spec number_of() -> non_neg_integer().
+
+number_of() ->
+ ?REGISTRY:number_of().
+
+
+%% *** which_sockets/0,1 ***
+%%
+%% Interface function to the socket registry
+%% Returns a list of all the sockets, accoring to the filter rule.
+%%
+-spec which_sockets() -> [socket()].
+
+which_sockets() ->
+ ?REGISTRY:which_sockets(fun(_) -> true end).
+
+-spec which_sockets(FilterRule) -> [socket()] when
+ FilterRule :: inet | inet6 |
+ stream | dgram | seqpacket |
+ sctp | tcp | udp |
+ pid() |
+ fun((socket_info()) -> boolean()).
+
+which_sockets(Domain)
+ when ((Domain =:= inet) orelse (Domain =:= inet6)) ->
+ ?REGISTRY:which_sockets(fun(#{domain := D}) when (D =:= Domain) -> true;
+ (_) -> false end);
+which_sockets(Type)
+ when ((Type =:= stream) orelse (Type =:= dgram) orelse (Type =:= seqpacket)) ->
+ ?REGISTRY:which_sockets(fun(#{type := T}) when (T =:= Type) -> true;
+ (_) -> false end);
+which_sockets(Proto)
+ when ((Proto =:= sctp) orelse (Proto =:= tcp) orelse (Proto =:= udp)) ->
+ ?REGISTRY:which_sockets(fun(#{protocol := P}) when (P =:= Proto) -> true;
+ (_) -> false end);
+which_sockets(CTRL)
+ when is_pid(CTRL) ->
+ ?REGISTRY:which_sockets(fun(#{ctrl := C}) when (C =:= CTRL) -> true;
+ (_) -> false end);
+which_sockets(Filter) when is_function(Filter, 1) ->
+ ?REGISTRY:which_sockets(Filter).
+
+
+%% ===========================================================================
+%%
+%% Debug features
+%%
+%% ===========================================================================
+
+
+-spec info() -> map().
+%%
+info() ->
+ prim_socket:info().
+
+
+-spec debug(D :: boolean()) -> ok.
+%%
+debug(D) when is_boolean(D) ->
+ prim_socket:debug(D).
+
+
+-spec socket_debug(D :: boolean()) -> ok.
+%%
+socket_debug(D) when is_boolean(D) ->
+ prim_socket:socket_debug(D).
+
+
+%% ===========================================================================
+%%
+%% info - Get miscellaneous information about a socket.
+%%
+%% Generates a list of various info about the socket, such as counter values.
+%%
+%% Do *not* call this function often.
+%%
+%% ===========================================================================
+
+-spec info(Socket) -> socket_info() when
+ Socket :: socket().
+%%
+info(?mk_socket(SockRef)) when is_reference(SockRef) ->
+ prim_socket:info(SockRef);
+info(Socket) ->
+ erlang:error(badarg, [Socket]).
+
+
+%% ===========================================================================
+%%
+%% 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.
+%%
+%% ===========================================================================
+
+-spec supports() -> [{Key1 :: term(),
+ boolean() | [{Key2 :: term(),
+ boolean() | [{Key3 :: term(),
+ boolean()}]}]}].
+supports() ->
+ [{Key1, supports(Key1)}
+ || Key1 <- [options, send_flags, recv_flags]]
+ ++ prim_socket:supports().
+
+-spec supports(Key1 :: term()) ->
+ [{Key2 :: term(),
+ boolean() | [{Key3 :: term(),
+ boolean()}]}].
+%%
+supports(options) ->
+ [{Level, supports(options, Level)}
+ || Level <- [socket, ip, ipv6, tcp, udp, sctp]];
+supports(Key) ->
+ prim_socket:supports(Key).
+
+-spec supports(Key1 :: term(), Key2 :: term()) ->
+ [{Key3 :: term(),
+ boolean()}].
+%%
+supports(Key1, Key2) ->
+ prim_socket:supports(Key1, Key2).
+
+
+-spec is_supported(Key1 :: term()) ->
+ boolean().
+is_supported(Key1) ->
+ get_is_supported(Key1, supports()).
+%%
+-spec is_supported(Key1 :: term(), Key2 :: term()) ->
+ boolean().
+is_supported(Key1, Key2) ->
+ get_is_supported(Key2, supports(Key1)).
+%%
+-spec is_supported(Key1 :: term(), Key2 :: term(), Key3 :: term()) ->
+ boolean().
+is_supported(Key1, Key2, Key3) ->
+ get_is_supported(Key3, supports(Key1, Key2)).
+
+
+get_is_supported(Key, Supported) ->
+ case lists:keyfind(Key, 1, Supported) of
+ false ->
+ false;
+ {_, Value} ->
+ if
+ is_boolean(Value) ->
+ Value;
+ is_list(Value) ->
+ false
+ end
+ end.
+
+
+%% ===========================================================================
+%%
+%% The proper socket API
+%%
+%% ===========================================================================
+
+%% ===========================================================================
+%%
+%% <KOLLA>
+%%
+%% 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 may therefor need monitor function(s):
+%%
+%% socket:monitor(Socket)
+%% socket:demonitor(Socket)
+%%
+%% </KOLLA>
+%%
+
+%% ===========================================================================
+%%
+%% open - create an endpoint for communication
+%%
+
+-spec open(FD) -> {ok, Socket} | {error, Reason} when
+ FD :: integer(),
+ Socket :: socket(),
+ Reason :: errcode().
+
+open(FD) when is_integer(FD) ->
+ open(FD, #{});
+open(FD) ->
+ erlang:error(badarg, [FD]).
+
+-spec open(FD, Opts) -> {ok, Socket} | {error, Reason} when
+ FD :: integer(),
+ Opts ::
+ #{domain => domain(),
+ type => type(),
+ protocol => protocol(),
+ dup => boolean()},
+ Socket :: socket(),
+ Reason :: errcode();
+
+ (Domain, Type) -> {ok, Socket} | {error, Reason} when
+ Domain :: domain(),
+ Type :: type(),
+ Socket :: socket(),
+ Reason :: errcode().
+
+open(FD, Opts) when is_integer(FD), is_map(Opts) ->
+ case prim_socket:open(FD, Opts) of
+ {ok, SockRef} ->
+ Socket = ?mk_socket(SockRef),
+ {ok, Socket};
+ {error, _} = ERROR ->
+ ERROR
+ end;
+open(Domain, Type) ->
+ open(Domain, Type, default).
+
+-spec open(Domain, Type, Protocol) -> {ok, Socket} | {error, Reason} when
+ Domain :: domain(),
+ Type :: type(),
+ Protocol :: default | protocol(),
+ Socket :: socket(),
+ Reason :: errcode().
+
+open(Domain, Type, Protocol) ->
+ open(Domain, Type, Protocol, #{}).
+
+-spec open(Domain, Type, Protocol, Opts) -> {ok, Socket} | {error, Reason} when
+ Domain :: domain(),
+ Type :: type(),
+ Protocol :: default | protocol(),
+ Opts :: map(),
+ Socket :: socket(),
+ Reason :: errcode().
+
+open(Domain, Type, Protocol, Opts) when is_map(Opts) ->
+ case prim_socket:open(Domain, Type, Protocol, Opts) of
+ {ok, SockRef} ->
+ Socket = ?mk_socket(SockRef),
+ {ok, Socket};
+ {error, _} = ERROR ->
+ ERROR
+ end;
+open(Domain, Type, Protocol, Opts) ->
+ erlang:error(badarg, [Domain, Type, Protocol, Opts]).
+
+
+%% ===========================================================================
+%%
+%% bind - bind a name (an address) to a socket
+%%
+%% Note that the short (atom) addresses only work for some domains,
+%% and that the nif will reject 'broadcast' for other domains than 'inet'
+%%
+
+-spec bind(Socket, Addr) -> {ok, Port} | {error, Reason} when
+ Socket :: socket(),
+ Addr :: sockaddr() | any | broadcast | loopback,
+ Port :: port_number(),
+ Reason :: inet:posix() | closed.
+
+bind(?mk_socket(SockRef) = Socket, Addr) when is_reference(SockRef) ->
+ if
+ is_map(Addr) ->
+ prim_socket:bind(SockRef, Addr);
+ %%
+ Addr =:= any;
+ Addr =:= broadcast;
+ Addr =:= loopback ->
+ case prim_socket:getopt(SockRef, otp, domain) of
+ {ok, Domain}
+ when Domain =:= inet;
+ Domain =:= inet6 ->
+ prim_socket:bind(
+ SockRef, #{family => Domain, addr => Addr});
+ {ok, _Domain} ->
+ {error, eafnosupport};
+ {error, _} = ERROR ->
+ ERROR
+ end;
+ %%
+ true ->
+ erlang:error(badarg, [Socket, Addr])
+ end;
+bind(Socket, Addr) ->
+ erlang:error(badarg, [Socket, 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 :: inet:posix() | closed.
+
+bind(?mk_socket(SockRef), Addrs, Action)
+ when is_reference(SockRef)
+ andalso is_list(Addrs)
+ andalso (Action =:= add
+ orelse Action =:= remove) ->
+ prim_socket:bind(SockRef, Addrs, Action);
+bind(Socket, Addrs, Action) ->
+ erlang:error(badarg, [Socket, Addrs, Action]).
+
+
+%% ===========================================================================
+%%
+%% connect - initiate a connection on a socket
+%%
+
+-spec connect(Socket) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Reason :: errcode() | closed.
+
+%% Finalize connect after connect(,, nowait) and received
+%% select message - see connect_deadline/3
+%%
+connect(?mk_socket(SockRef))
+ when is_reference(SockRef) ->
+ prim_socket:connect(SockRef);
+connect(Socket) ->
+ erlang:error(badarg, [Socket]).
+
+
+-spec connect(Socket, SockAddr) -> ok | {error, Reason} when
+ Socket :: socket(),
+ SockAddr :: sockaddr(),
+ Reason :: errcode() | closed.
+
+connect(Socket, SockAddr) ->
+ connect(Socket, SockAddr, infinity).
+
+
+-spec connect(Socket, SockAddr, nowait) ->
+ ok | {select, SelectInfo} | {error, Reason} when
+ Socket :: socket(),
+ SockAddr :: sockaddr(),
+ SelectInfo :: select_info(),
+ Reason :: errcode() | closed;
+
+ (Socket, SockAddr, Timeout) -> ok | {error, Reason} when
+ Socket :: socket(),
+ SockAddr :: sockaddr(),
+ Timeout :: timeout(),
+ Reason :: errcode() | closed | timeout.
+
+%% <KOLLA>
+%% Is it possible to connect with family = local for the (dest) sockaddr?
+%% </KOLLA>
+connect(?mk_socket(SockRef) = Socket, SockAddr, Timeout)
+ when is_reference(SockRef) ->
+ case deadline(Timeout) of
+ badarg = Reason ->
+ erlang:error(Reason, [Socket, SockAddr, Timeout]);
+ nowait ->
+ connect_nowait(SockRef, SockAddr);
+ Deadline ->
+ connect_deadline(SockRef, SockAddr, Deadline)
+ end;
+connect(Socket, SockAddr, Timeout) ->
+ erlang:error(badarg, [Socket, SockAddr, Timeout]).
+
+connect_nowait(SockRef, SockAddr) ->
+ case prim_socket:connect(SockRef, SockAddr) of
+ {select, Ref} ->
+ ?SELECT(connect, Ref);
+ Result ->
+ Result
+ end.
+
+connect_deadline(SockRef, SockAddr, Deadline) ->
+ case prim_socket:connect(SockRef, SockAddr) of
+ {select, Ref} ->
+ %% Connecting...
+ Timeout = timeout(Deadline),
+ receive
+ ?mk_socket_msg(_Socket, select, Ref) ->
+ prim_socket:connect(SockRef);
+ ?mk_socket_msg(_Socket, abort, {Ref, Reason}) ->
+ {error, Reason}
+ after Timeout ->
+ cancel(SockRef, connect, Ref),
+ {error, timeout}
+ end;
+ Result ->
+ Result
+ end.
+
+
+%% ===========================================================================
+%%
+%% listen - listen for connections on a socket
+%%
+
+-spec listen(Socket) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Reason :: inet:posix() | closed.
+
+listen(Socket) ->
+ listen(Socket, ?ESOCK_LISTEN_BACKLOG_DEFAULT).
+
+-spec listen(Socket, Backlog) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Backlog :: integer(),
+ Reason :: inet:posix() | closed.
+
+listen(?mk_socket(SockRef), Backlog)
+ when is_reference(SockRef), is_integer(Backlog) ->
+ prim_socket:listen(SockRef, Backlog);
+listen(Socket, Backlog) ->
+ erlang:error(badarg, [Socket, Backlog]).
+
+
+%% ===========================================================================
+%%
+%% accept, accept4 - accept a connection on a socket
+%%
+
+-spec accept(LSocket) -> {ok, Socket} | {error, Reason} when
+ LSocket :: socket(),
+ Socket :: socket(),
+ Reason :: errcode() | closed.
+
+accept(Socket) ->
+ accept(Socket, ?ESOCK_ACCEPT_TIMEOUT_DEFAULT).
+
+-spec accept(LSocket, nowait) ->
+ {ok, Socket} |
+ {select, SelectInfo} |
+ {error, Reason} when
+ LSocket :: socket(),
+ Socket :: socket(),
+ SelectInfo :: select_info(),
+ Reason :: errcode() | closed;
+
+ (LSocket, Timeout) -> {ok, Socket} | {error, Reason} when
+ LSocket :: socket(),
+ Timeout :: timeout(),
+ Socket :: socket(),
+ Reason :: errcode() | closed | timeout.
+
+accept(?mk_socket(LSockRef) = Socket, Timeout)
+ when is_reference(LSockRef) ->
+ case deadline(Timeout) of
+ badarg = Reason ->
+ erlang:error(Reason, [Socket, Timeout]);
+ nowait ->
+ accept_nowait(LSockRef);
+ Deadline ->
+ accept_deadline(LSockRef, Deadline)
+ end;
+accept(Socket, Timeout) ->
+ erlang:error(badarg, [Socket, Timeout]).
+
+accept_nowait(LSockRef) ->
+ AccRef = make_ref(),
+ case prim_socket:accept(LSockRef, AccRef) of
+ select ->
+ ?SELECT(accept, AccRef);
+ Result ->
+ accept_result(LSockRef, AccRef, Result)
+ end.
+
+accept_deadline(LSockRef, Deadline) ->
+ AccRef = make_ref(),
+ case prim_socket:accept(LSockRef, AccRef) of
+ select ->
+ %% Each call is non-blocking, but even then it takes
+ %% *some* time, so just to be sure, recalculate before
+ %% the receive.
+ Timeout = timeout(Deadline),
+ receive
+ ?mk_socket_msg(?mk_socket(LSockRef), select, AccRef) ->
+ accept_deadline(LSockRef, Deadline);
+ ?mk_socket_msg(_Socket, abort, {AccRef, Reason}) ->
+ {error, Reason}
+ after Timeout ->
+ cancel(LSockRef, accept, AccRef),
+ {error, timeout}
+ end;
+ Result ->
+ accept_result(LSockRef, AccRef, Result)
+ end.
+
+accept_result(LSockRef, AccRef, Result) ->
+ case Result of
+ {ok, SockRef} ->
+ Socket = ?mk_socket(SockRef),
+ {ok, Socket};
+ {error, _} = ERROR ->
+ cancel(LSockRef, accept, AccRef), % Just to be on the safe side...
+ ERROR
+ end.
+
+
+%% ===========================================================================
+%%
+%% 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, ?ESOCK_SEND_FLAGS_DEFAULT, ?ESOCK_SEND_TIMEOUT_DEFAULT).
+
+-spec send(Socket, Data, Flags) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Data :: iodata(),
+ Flags :: send_flags(),
+ Reason :: {errcode() | closed,
+ Remaining :: pos_integer()};
+
+ (Socket, Data, Timeout :: nowait) ->
+ ok |
+ {ok, {binary(), SelectInfo}} |
+ {select, SelectInfo} |
+ {ok, {RestData, SelectInfo}} |
+ {error, Reason} when
+ Socket :: socket(),
+ Data :: iodata(),
+ RestData :: binary(),
+ SelectInfo :: select_info(),
+ Reason :: {errcode() | closed,
+ Remaining :: pos_integer()};
+
+ (Socket, Data, Timeout) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Data :: iodata(),
+ Timeout :: timeout(),
+ Reason :: {errcode() | closed | timeout,
+ Remaining :: pos_integer()}.
+
+send(Socket, Data, Flags) when is_list(Flags) ->
+ send(Socket, Data, Flags, ?ESOCK_SEND_TIMEOUT_DEFAULT);
+send(Socket, Data, Timeout) ->
+ send(Socket, Data, ?ESOCK_SEND_FLAGS_DEFAULT, Timeout).
+
+-spec send(Socket, Data, Flags, nowait) -> ok |
+ {select, SelectInfo} |
+ {ok, {RestData, SelectInfo}} |
+ {error, Reason} when
+ Socket :: socket(),
+ Data :: iodata(),
+ Flags :: send_flags(),
+ RestData :: binary(),
+ SelectInfo :: select_info(),
+ Reason :: {errcode() | closed,
+ Remaining :: pos_integer()};
+
+ (Socket, Data, Flags, Timeout) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Data :: iodata(),
+ Flags :: send_flags(),
+ Timeout :: timeout(),
+ Reason :: {errcode() | closed | timeout,
+ Remaining :: pos_integer()}.
+
+send(Socket, Data, Flags, Timeout) when is_list(Data) ->
+ Bin = erlang:list_to_binary(Data),
+ send(Socket, Bin, Flags, Timeout);
+send(?mk_socket(SockRef) = Socket, Data, Flags, Timeout)
+ when is_reference(SockRef), is_binary(Data), is_list(Flags) ->
+ To = undefined,
+ case deadline(Timeout) of
+ badarg = Reason ->
+ erlang:error(Reason, [Socket, Data, Flags, Timeout]);
+ nowait ->
+ send_common_nowait(SockRef, Data, To, Flags, send);
+ Deadline ->
+ send_common_deadline(SockRef, Data, To, Flags, Deadline, send)
+ end;
+send(Socket, Data, Flags, Timeout) ->
+ erlang:error(badarg, [Socket, Data, Flags, Timeout]).
+
+send_common_nowait(SockRef, Data, To, Flags, SendName) ->
+ SendRef = make_ref(),
+ case
+ case SendName of
+ send ->
+ prim_socket:send(SockRef, SendRef, Data, Flags);
+ sendto ->
+ prim_socket:sendto(SockRef, SendRef, Data, To, Flags)
+ end
+ of
+ {ok, Written} ->
+ %% We are partially done, but the user don't want to wait (here)
+ %% for completion
+ <<_:Written/binary, Rest/binary>> = Data,
+ {ok, {Rest, ?SELECT_INFO(SendName, SendRef)}};
+ select ->
+ ?SELECT(SendName, SendRef);
+ Result ->
+ send_common_result(Data, Result)
+ end.
+
+send_common_deadline(SockRef, Data, To, Flags, Deadline, SendName) ->
+ SendRef = make_ref(),
+ case
+ case SendName of
+ send ->
+ prim_socket:send(SockRef, SendRef, Data, Flags);
+ sendto ->
+ prim_socket:sendto(SockRef, SendRef, Data, To, Flags)
+ end
+ of
+ {ok, Written} ->
+ %% We are partially done, wait for continuation
+ Timeout = timeout(Deadline),
+ receive
+ ?mk_socket_msg(_Socket, select, SendRef)
+ when (Written > 0) ->
+ <<_:Written/binary, Rest/binary>> = Data,
+ send_common_deadline(
+ SockRef, Rest, To, Flags, Deadline, SendName);
+ ?mk_socket_msg(_Socket, select, SendRef) ->
+ send_common_deadline(
+ SockRef, Data, To, Flags, Deadline, SendName);
+ ?mk_socket_msg(_Socket, abort, {SendRef, Reason}) ->
+ {error, {Reason, byte_size(Data)}}
+ after Timeout ->
+ _ = cancel(SockRef, SendName, SendRef),
+ {error, {timeout, byte_size(Data)}}
+ end;
+ select ->
+ %% Wait for continuation
+ Timeout = timeout(Deadline),
+ receive
+ ?mk_socket_msg(_Socket, select, SendRef) ->
+ send_common_deadline(
+ SockRef, Data, To, Flags, Deadline, SendName);
+ ?mk_socket_msg(_Socket, abort, {SendRef, Reason}) ->
+ {error, {Reason, byte_size(Data)}}
+ after Timeout ->
+ _ = cancel(SockRef, SendName, SendRef),
+ {error, {timeout, byte_size(Data)}}
+ end;
+ %%
+ {error, ealready = Reason} ->
+ %% Internal error:
+ %% we called send, got eagain, and called send again
+ %% - without waiting for select message
+ erlang:error(Reason);
+ Result ->
+ send_common_result(Data, Result)
+ end.
+
+send_common_result(Data, Result) ->
+ case Result of
+ ok ->
+ ok;
+ {error, Reason} ->
+ {error, {Reason, byte_size(Data)}}
+ end.
+
+
+%% ---------------------------------------------------------------------------
+%%
+
+-spec sendto(Socket, Data, Dest) ->
+ ok | {error, Reason} when
+ Socket :: socket(),
+ Data :: binary(),
+ Dest :: sockaddr(),
+ Reason :: {errcode() | closed,
+ Remaining :: pos_integer()}.
+
+sendto(Socket, Data, Dest) ->
+ sendto(Socket, Data, Dest, ?ESOCK_SENDTO_FLAGS_DEFAULT).
+
+-spec sendto(Socket, Data, Dest, Flags) ->
+ ok | {error, Reason} when
+ Socket :: socket(),
+ Data :: binary(),
+ Dest :: sockaddr(),
+ Flags :: send_flags(),
+ Reason :: {errcode() | closed,
+ Remaining :: pos_integer()};
+
+ (Socket, Data, Dest, Timeout :: nowait) ->
+ ok |
+ {select, SelectInfo} |
+ {error, Reason} when
+ Socket :: socket(),
+ Data :: iodata(),
+ Dest :: sockaddr(),
+ SelectInfo :: select_info(),
+ Reason :: {errcode() | closed,
+ Remaining :: pos_integer()};
+
+ (Socket, Data, Dest, Timeout) ->
+ ok | {error, Reason} when
+ Socket :: socket(),
+ Data :: iodata(),
+ Dest :: sockaddr(),
+ Timeout :: timeout(),
+ Reason :: {errcode() | closed | timeout,
+ Remaining :: pos_integer()}.
+
+sendto(Socket, Data, Dest, Flags) when is_list(Flags) ->
+ sendto(Socket, Data, Dest, Flags, ?ESOCK_SENDTO_TIMEOUT_DEFAULT);
+sendto(Socket, Data, Dest, Timeout) ->
+ sendto(Socket, Data, Dest, ?ESOCK_SENDTO_FLAGS_DEFAULT, Timeout).
+
+
+-spec sendto(Socket, Data, Dest, Flags, nowait) ->
+ ok |
+ {ok, {binary(), SelectInfo}} |
+ {select, SelectInfo} |
+ {error, Reason} when
+ Socket :: socket(),
+ Data :: binary(),
+ Dest :: sockaddr(),
+ Flags :: send_flags(),
+ SelectInfo :: select_info(),
+ Reason :: {errcode() | closed,
+ Remaining :: pos_integer()};
+
+ (Socket, Data, Dest, Flags, Timeout) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Data :: binary(),
+ Dest :: sockaddr(),
+ Flags :: send_flags(),
+ Timeout :: timeout(),
+ Reason :: {errcode() | closed | timeout,
+ Remaining :: pos_integer()}.
+
+sendto(Socket, Data, Dest, Flags, Timeout) when is_list(Data) ->
+ Bin = erlang:list_to_binary(Data),
+ sendto(Socket, Bin, Dest, Flags, Timeout);
+sendto(?mk_socket(SockRef) = Socket, Data, Dest, Flags, Timeout)
+ when is_reference(SockRef), is_binary(Data), is_list(Flags) ->
+ case deadline(Timeout) of
+ badarg = Reason ->
+ erlang:error(Reason, [Socket, Data, Dest, Flags, Timeout]);
+ nowait ->
+ send_common_nowait(SockRef, Data, Dest, Flags, sendto);
+ Deadline ->
+ send_common_deadline(
+ SockRef, Data, Dest, Flags, Deadline, sendto)
+ end;
+sendto(Socket, Data, Dest, Flags, Timeout) ->
+ erlang:error(badarg, [Socket, Data, Dest, Flags, 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) ->
+ ok |
+ {ok, Remaining} |
+ {error, Reason} when
+ Socket :: socket(),
+ MsgHdr :: msghdr(),
+ Remaining :: erlang:iovec(),
+ Reason :: term().
+
+sendmsg(Socket, MsgHdr) ->
+ sendmsg(Socket, MsgHdr,
+ ?ESOCK_SENDMSG_FLAGS_DEFAULT, ?ESOCK_SENDMSG_TIMEOUT_DEFAULT).
+
+
+-spec sendmsg(Socket, MsgHdr, Flags) -> ok | {error, Reason} when
+ Socket :: socket(),
+ MsgHdr :: msghdr(),
+ Flags :: send_flags(),
+ Reason :: errcode() | closed;
+
+ (Socket, MsgHdr, Timeout :: nowait) ->
+ ok |
+ {ok, Remaining} |
+ {error, Reason} when
+ Socket :: socket(),
+ MsgHdr :: msghdr(),
+ Remaining :: erlang:iovec(),
+ Reason :: errcode() | closed;
+
+ (Socket, MsgHdr, Timeout) -> ok | {error, Reason} when
+ Socket :: socket(),
+ MsgHdr :: msghdr(),
+ Timeout :: timeout(),
+ Reason :: errcode() | closed | timeout.
+
+sendmsg(Socket, MsgHdr, Flags) when is_list(Flags) ->
+ sendmsg(Socket, MsgHdr, Flags, ?ESOCK_SENDMSG_TIMEOUT_DEFAULT);
+sendmsg(Socket, MsgHdr, Timeout) ->
+ sendmsg(Socket, MsgHdr, ?ESOCK_SENDMSG_FLAGS_DEFAULT, Timeout).
+
+
+-spec sendmsg(Socket, MsgHdr, Flags, nowait) ->
+ ok |
+ {ok, Remaining} |
+ {error, Reason} when
+ Socket :: socket(),
+ MsgHdr :: msghdr(),
+ Flags :: send_flags(),
+ Remaining :: erlang:iovec(),
+ Reason :: errcode() | closed;
+
+ (Socket, MsgHdr, Flags, Timeout) ->
+ ok |
+ {ok, Remaining} |
+ {error, Reason} when
+ Socket :: socket(),
+ MsgHdr :: msghdr(),
+ Flags :: send_flags(),
+ Timeout :: timeout(),
+ Remaining :: erlang:iovec(),
+ Reason :: errcode() | closed | timeout.
+
+sendmsg(?mk_socket(SockRef) = Socket, MsgHdr, Flags, Timeout)
+ when is_reference(SockRef), is_map(MsgHdr), is_list(Flags) ->
+ case deadline(Timeout) of
+ badarg = Reason ->
+ erlang:error(Reason, [Socket, MsgHdr, Flags, Timeout]);
+ Deadline ->
+ sendmsg_loop(SockRef, MsgHdr, Flags, Deadline)
+ end;
+sendmsg(Socket, MsgHdr, Flags, Timeout) ->
+ erlang:error(badarg, [Socket, MsgHdr, Flags, Timeout]).
+
+sendmsg_loop(SockRef, MsgHdr, Flags, Deadline) ->
+ SendRef = make_ref(),
+ case prim_socket:sendmsg(SockRef, SendRef, MsgHdr, Flags) of
+ ok ->
+ %% 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 need to cancel this partial write.
+ %%
+ _ = cancel(SockRef, sendmsg, SendRef),
+ {ok, sendmsg_rest(maps:get(iov, MsgHdr), Written)};
+ %%
+ select when (Deadline =:= nowait) ->
+ ?SELECT(sendmsg, SendRef);
+ select ->
+ Timeout = timeout(Deadline),
+ receive
+ ?mk_socket_msg(?mk_socket(SockRef), select, SendRef) ->
+ sendmsg_loop(SockRef, MsgHdr, Flags, Deadline);
+ ?mk_socket_msg(_Socket, abort, {SendRef, Reason}) ->
+ {error, Reason}
+ after Timeout ->
+ _ = cancel(SockRef, sendmsg, SendRef),
+ {error, timeout}
+ end;
+ %%
+ {error, ealready = Reason} when Deadline =/= nowait ->
+ %% Internal error:
+ %% we called send, got eagain, and called send again
+ %% - without waiting for select message
+ erlang:error(Reason);
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+sendmsg_rest([B|IOVec], Written) when Written >= byte_size(B) ->
+ sendmsg_rest(IOVec, Written - byte_size(B));
+sendmsg_rest([B|IOVec], Written) ->
+ <<_:Written/binary, Rest/binary>> = B,
+ [Rest|IOVec].
+
+
+%% ===========================================================================
+%%
+%% 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.
+
+-spec recv(Socket) ->
+ {ok, Data} |
+ {error, Reason} when
+ Socket :: socket(),
+ Data :: binary(),
+ Reason ::
+ errcode() | closed |
+ {errcode() | closed, Data :: binary()}.
+
+recv(Socket) ->
+ recv(Socket, 0).
+
+-spec recv(Socket, Length) ->
+ {ok, Data} |
+ {error, Reason} when
+ Socket :: socket(),
+ Length :: non_neg_integer(),
+ Data :: binary(),
+ Reason ::
+ errcode() | closed |
+ {errcode() | closed, Data :: binary()}.
+
+recv(Socket, Length) ->
+ recv(Socket, Length,
+ ?ESOCK_RECV_FLAGS_DEFAULT,
+ ?ESOCK_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 ::
+ errcode() | closed |
+ {errcode() | closed, Data :: binary()};
+
+ (Socket, Length, Timeout :: nowait) ->
+ {ok, Data} |
+ {ok, {Data, SelectInfo}} |
+ {select, SelectInfo} |
+ {error, Reason} when
+ Socket :: socket(),
+ Length :: non_neg_integer(),
+ Data :: binary(),
+ SelectInfo :: select_info(),
+ Reason ::
+ errcode() | closed |
+ {errcode() | closed, Data :: binary()};
+
+ (Socket, Length, Timeout) ->
+ {ok, Data} |
+ {error, Reason} when
+ Socket :: socket(),
+ Length :: non_neg_integer(),
+ Timeout :: timeout(),
+ Data :: binary(),
+ Reason ::
+ errcode() | closed | timeout |
+ {errcode() | closed | timeout, Data :: binary()}.
+
+recv(Socket, Length, Flags) when is_list(Flags) ->
+ recv(Socket, Length, Flags, ?ESOCK_RECV_TIMEOUT_DEFAULT);
+recv(Socket, Length, Timeout) ->
+ recv(Socket, Length, ?ESOCK_RECV_FLAGS_DEFAULT, Timeout).
+
+-spec recv(Socket, Length, Flags, nowait) ->
+ {ok, Data} |
+ {ok, {Data, SelectInfo}} |
+ {select, SelectInfo} |
+ {error, Reason} when
+ Socket :: socket(),
+ Length :: non_neg_integer(),
+ Flags :: recv_flags(),
+ Data :: binary(),
+ SelectInfo :: select_info(),
+ Reason ::
+ errcode() | closed |
+ {errcode() | closed, Data :: binary()};
+
+ (Socket, Length, Flags, Timeout) ->
+ {ok, Data} |
+ {error, Reason} when
+ Socket :: socket(),
+ Length :: non_neg_integer(),
+ Flags :: recv_flags(),
+ Timeout :: timeout(),
+ Data :: binary(),
+ Reason ::
+ errcode() | closed | timeout |
+ {errcode() | closed | timeout, Data :: binary()}.
+
+recv(?mk_socket(SockRef) = Socket, Length, Flags, Timeout)
+ when is_reference(SockRef),
+ is_integer(Length), Length >= 0,
+ is_list(Flags) ->
+ case deadline(Timeout) of
+ badarg = Reason ->
+ erlang:error(Reason, [Socket, Length, Flags, Timeout]);
+ nowait ->
+ recv_nowait(SockRef, Length, Flags, <<>>);
+ Deadline ->
+ recv_deadline(SockRef, Length, Flags, Deadline, <<>>)
+ end;
+recv(Socket, Length, Flags, Timeout) ->
+ erlang:error(badarg, [Socket, Length, Flags, Timeout]).
+
+%% We will only recurse with Length == 0 if Length is 0,
+%% so Length == 0 means to return all available data also when recursing
+
+recv_nowait(SockRef, Length, Flags, Acc) ->
+ RecvRef = make_ref(),
+ case prim_socket:recv(SockRef, RecvRef, Length, Flags) of
+ {more, Bin} ->
+ %% We got what we requested but will not waste more time
+ %% although there might be more data available
+ {ok, bincat(Acc, Bin)};
+ {select, Bin} ->
+ %% We got less than requested so the caller will
+ %% get a select message when there might be more to read
+ {ok, {bincat(Acc, Bin), ?SELECT_INFO(recv, RecvRef)}};
+ select ->
+ %% The caller will get a select message when there
+ %% might me data to read
+ if
+ byte_size(Acc) =:= 0 ->
+ ?SELECT(recv, RecvRef);
+ true ->
+ {ok, {Acc, ?SELECT_INFO(recv, RecvRef)}}
+ end;
+ Result ->
+ recv_result(Acc, Result)
+ end.
+
+recv_deadline(SockRef, Length, Flags, Deadline, Acc) ->
+ RecvRef = make_ref(),
+ case prim_socket:recv(SockRef, RecvRef, Length, Flags) of
+ {more, Bin} ->
+ %% There is more data readily available
+ %% - repeat unless time's up
+ Timeout = timeout(Deadline),
+ if
+ 0 < Timeout ->
+ %% Recv more
+ recv_deadline(
+ SockRef, Length, Flags, Deadline, bincat(Acc, Bin));
+ true ->
+ {ok, bincat(Acc, Bin)}
+ end;
+ %%
+ {select, Bin} ->
+ %% We got less than requested
+ Timeout = timeout(Deadline),
+ receive
+ ?mk_socket_msg(?mk_socket(SockRef), select, RecvRef) ->
+ if
+ 0 < Timeout ->
+ %% Recv more
+ recv_deadline(
+ SockRef, Length - byte_size(Bin), Flags,
+ Deadline, bincat(Acc, Bin));
+ true ->
+ {error, {timeout, bincat(Acc, Bin)}}
+ end;
+ ?mk_socket_msg(_Socket, abort, {RecvRef, Reason}) ->
+ {error, {Reason, bincat(Acc, Bin)}}
+ after Timeout ->
+ cancel(SockRef, recv, RecvRef),
+ {error, {timeout, bincat(Acc, Bin)}}
+ end;
+ %%
+ select when Length =:= 0, 0 < byte_size(Acc) ->
+ %% We first got some data and are then asked to wait,
+ %% but we only want the first that comes
+ %% - cancel and return what we have
+ cancel(SockRef, recv, RecvRef),
+ {ok, Acc};
+ select ->
+ %% There is nothing just now, but we will be notified when there
+ %% is something to read (a select message).
+ Timeout = timeout(Deadline),
+ receive
+ ?mk_socket_msg(?mk_socket(SockRef), select, RecvRef) ->
+ if
+ 0 < Timeout ->
+ %% Retry
+ recv_deadline(
+ SockRef, Length, Flags, Deadline, Acc);
+ true ->
+ recv_error(Acc, timeout)
+ end;
+ ?mk_socket_msg(_Socket, abort, {RecvRef, Reason}) ->
+ recv_error(Acc, Reason)
+ after Timeout ->
+ cancel(SockRef, recv, RecvRef),
+ recv_error(Acc, timeout)
+ end;
+ %%
+ {error, ealready = Reason} ->
+ %% Internal error:
+ %% we called recv, got eagain, and called recv again
+ %% - without waiting for select message
+ erlang:error(Reason);
+ Result ->
+ recv_result(Acc, Result)
+ end.
+
+recv_result(Acc, Result) ->
+ case Result of
+ {ok, Bin} ->
+ {ok, bincat(Acc, Bin)};
+ {error, _} = ERROR when byte_size(Acc) =:= 0 ->
+ ERROR;
+ {error, Reason} ->
+ {error, {Reason, Acc}}
+ end.
+
+recv_error(Acc, Reason) ->
+ if
+ byte_size(Acc) =:= 0 ->
+ {error, Reason};
+ true ->
+ {error, {Reason, Acc}}
+ end.
+
+%% ---------------------------------------------------------------------------
+%%
+%% 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 (OS) buffers, so another recvfrom call
+%% 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 :: errcode() | closed.
+
+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 :: errcode() | closed.
+
+recvfrom(Socket, BufSz) ->
+ recvfrom(Socket, BufSz,
+ ?ESOCK_RECV_FLAGS_DEFAULT,
+ ?ESOCK_RECV_TIMEOUT_DEFAULT).
+
+-spec recvfrom(Socket, Flags, nowait) ->
+ {ok, {Source, Data}} |
+ {select, SelectInfo} |
+ {error, Reason} when
+ Socket :: socket(),
+ Flags :: recv_flags(),
+ Source :: sockaddr() | undefined,
+ Data :: binary(),
+ SelectInfo :: select_info(),
+ Reason :: errcode() | closed;
+
+ (Socket, Flags, Timeout) ->
+ {ok, {Source, Data}} |
+ {error, Reason} when
+ Socket :: socket(),
+ Flags :: recv_flags(),
+ Timeout :: timeout(),
+ Source :: sockaddr() | undefined,
+ Data :: binary(),
+ Reason :: errcode() | closed | timeout;
+
+ (Socket, BufSz, Flags) ->
+ {ok, {Source, Data}} | {error, Reason} when
+ Socket :: socket(),
+ BufSz :: non_neg_integer(),
+ Flags :: recv_flags(),
+ Source :: sockaddr() | undefined,
+ Data :: binary(),
+ Reason :: errcode() | closed;
+
+ (Socket, BufSz, nowait) ->
+ {ok, {Source, Data}} |
+ {select, SelectInfo} |
+ {error, Reason} when
+ Socket :: socket(),
+ BufSz :: non_neg_integer(),
+ Source :: sockaddr() | undefined,
+ Data :: binary(),
+ SelectInfo :: select_info(),
+ Reason :: errcode() | closed;
+
+ (Socket, BufSz, Timeout) ->
+ {ok, {Source, Data}} | {error, Reason} when
+ Socket :: socket(),
+ BufSz :: non_neg_integer(),
+ Timeout :: timeout(),
+ Source :: sockaddr() | undefined,
+ Data :: binary(),
+ Reason :: errcode() | closed | timeout.
+
+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, ?ESOCK_RECV_TIMEOUT_DEFAULT);
+recvfrom(Socket, BufSz, Timeout) ->
+ recvfrom(Socket, BufSz, ?ESOCK_RECV_FLAGS_DEFAULT, Timeout).
+
+-spec recvfrom(Socket, BufSz, Flags, nowait) ->
+ {ok, {Source, Data}} |
+ {select, SelectInfo} |
+ {error, Reason} when
+ Socket :: socket(),
+ BufSz :: non_neg_integer(),
+ Flags :: recv_flags(),
+ Source :: sockaddr() | undefined,
+ Data :: binary(),
+ SelectInfo :: select_info(),
+ Reason :: errcode() | closed;
+
+ (Socket, BufSz, Flags, Timeout) ->
+ {ok, {Source, Data}} |
+ {error, Reason} when
+ Socket :: socket(),
+ BufSz :: non_neg_integer(),
+ Flags :: recv_flags(),
+ Timeout :: timeout(),
+ Source :: sockaddr() | undefined,
+ Data :: binary(),
+ Reason :: errcode() | closed | timeout.
+
+recvfrom(?mk_socket(SockRef) = Socket, BufSz, Flags, Timeout)
+ when is_reference(SockRef),
+ is_integer(BufSz), 0 =< BufSz,
+ is_list(Flags) ->
+ case deadline(Timeout) of
+ badarg = Reason ->
+ erlang:error(Reason, [Socket, BufSz, Flags, Timeout]);
+ nowait ->
+ recvfrom_nowait(SockRef, BufSz, Flags);
+ Deadline ->
+ recvfrom_deadline(SockRef, BufSz, Flags, Deadline)
+ end;
+recvfrom(Socket, BufSz, Flags, Timeout) ->
+ erlang:error(badarg, [Socket, BufSz, Flags, Timeout]).
+
+recvfrom_nowait(SockRef, BufSz, Flags) ->
+ RecvRef = make_ref(),
+ case prim_socket:recvfrom(SockRef, RecvRef, BufSz, Flags) of
+ select ->
+ ?SELECT(recvfrom, RecvRef);
+ Result ->
+ recvfrom_result(Result)
+ end.
+
+recvfrom_deadline(SockRef, BufSz, Flags, Deadline) ->
+ RecvRef = make_ref(),
+ case prim_socket:recvfrom(SockRef, RecvRef, BufSz, Flags) of
+ select ->
+ %% There is nothing just now, but we will be notified when there
+ %% is something to read (a select message).
+ Timeout = timeout(Deadline),
+ receive
+ ?mk_socket_msg(?mk_socket(SockRef), select, RecvRef) ->
+ recvfrom_deadline(SockRef, BufSz, Flags, Deadline);
+ ?mk_socket_msg(_Socket, abort, {RecvRef, Reason}) ->
+ {error, Reason}
+ after Timeout ->
+ cancel(SockRef, recvfrom, RecvRef),
+ {error, timeout}
+ end;
+ {error, ealready = Reason} ->
+ %% Internal error:
+ %% we called recvfrom, got eagain, and called recvfrom again
+ %% - without waiting for select message
+ erlang:error(Reason);
+ Result ->
+ recvfrom_result(Result)
+ end.
+
+recvfrom_result(Result) ->
+ case Result of
+ {ok, {_Source, _NewData}} = OK ->
+ OK;
+ {error, _Reason} = ERROR ->
+ ERROR
+ end.
+
+
+%% ---------------------------------------------------------------------------
+%%
+
+-spec recvmsg(Socket) -> {ok, MsgHdr} | {error, Reason} when
+ Socket :: socket(),
+ MsgHdr :: msghdr(),
+ Reason :: errcode() | closed.
+
+recvmsg(Socket) ->
+ recvmsg(Socket, 0, 0,
+ ?ESOCK_RECV_FLAGS_DEFAULT, ?ESOCK_RECV_TIMEOUT_DEFAULT).
+
+-spec recvmsg(Socket, Flags) -> {ok, MsgHdr} | {error, Reason} when
+ Socket :: socket(),
+ Flags :: recv_flags(),
+ MsgHdr :: msghdr(),
+ Reason :: errcode() | closed;
+
+ (Socket, Timeout :: nowait) -> {ok, MsgHdr} |
+ {select, SelectInfo} |
+ {error, Reason} when
+ Socket :: socket(),
+ MsgHdr :: msghdr(),
+ SelectInfo :: select_info(),
+ Reason :: errcode() | closed;
+
+ (Socket, Timeout) -> {ok, MsgHdr} | {error, Reason} when
+ Socket :: socket(),
+ Timeout :: timeout(),
+ MsgHdr :: msghdr(),
+ Reason :: errcode() | closed | timeout.
+
+recvmsg(Socket, Flags) when is_list(Flags) ->
+ recvmsg(Socket, 0, 0, Flags, ?ESOCK_RECV_TIMEOUT_DEFAULT);
+recvmsg(Socket, Timeout) ->
+ recvmsg(Socket, 0, 0, ?ESOCK_RECV_FLAGS_DEFAULT, Timeout).
+
+-spec recvmsg(Socket, Flags, nowait) -> {ok, MsgHdr} |
+ {select, SelectInfo} |
+ {error, Reason} when
+ Socket :: socket(),
+ Flags :: recv_flags(),
+ MsgHdr :: msghdr(),
+ SelectInfo :: select_info(),
+ Reason :: errcode() | closed;
+
+ (Socket, Flags, Timeout) -> {ok, MsgHdr} | {error, Reason} when
+ Socket :: socket(),
+ Flags :: recv_flags(),
+ Timeout :: timeout(),
+ MsgHdr :: msghdr(),
+ Reason :: errcode() | closed | timeout;
+
+ (Socket, BufSz, CtrlSz) -> {ok, MsgHdr} | {error, Reason} when
+ Socket :: socket(),
+ BufSz :: non_neg_integer(),
+ CtrlSz :: non_neg_integer(),
+ MsgHdr :: msghdr(),
+ Reason :: errcode() | closed.
+
+recvmsg(Socket, Flags, Timeout) when is_list(Flags) ->
+ recvmsg(Socket, 0, 0, Flags, Timeout);
+recvmsg(Socket, BufSz, CtrlSz) when is_integer(BufSz), is_integer(CtrlSz) ->
+ recvmsg(Socket, BufSz, CtrlSz,
+ ?ESOCK_RECV_FLAGS_DEFAULT, ?ESOCK_RECV_TIMEOUT_DEFAULT).
+
+
+-spec recvmsg(Socket, BufSz, CtrlSz, Flags, nowait) ->
+ {ok, MsgHdr} |
+ {select, SelectInfo} |
+ {error, Reason} when
+ Socket :: socket(),
+ BufSz :: non_neg_integer(),
+ CtrlSz :: non_neg_integer(),
+ Flags :: recv_flags(),
+ MsgHdr :: msghdr(),
+ SelectInfo :: select_info(),
+ Reason :: errcode() | closed;
+
+ (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 :: errcode() | closed | timeout.
+
+recvmsg(?mk_socket(SockRef) = Socket, BufSz, CtrlSz, Flags, Timeout)
+ when is_reference(SockRef),
+ is_integer(BufSz), 0 =< BufSz,
+ is_integer(CtrlSz), 0 =< CtrlSz,
+ is_list(Flags) ->
+ case deadline(Timeout) of
+ badarg = Reason ->
+ erlang:error(Reason, [Socket, BufSz, CtrlSz, Flags, Timeout]);
+ nowait ->
+ recvmsg_nowait(SockRef, BufSz, CtrlSz, Flags);
+ Deadline ->
+ recvmsg_deadline(SockRef, BufSz, CtrlSz, Flags, Deadline)
+ end;
+recvmsg(Socket, BufSz, CtrlSz, Flags, Timeout) ->
+ erlang:error(badarg, [Socket, BufSz, CtrlSz, Flags, Timeout]).
+
+recvmsg_nowait(SockRef, BufSz, CtrlSz, Flags) ->
+ RecvRef = make_ref(),
+ case prim_socket:recvmsg(SockRef, RecvRef, BufSz, CtrlSz, Flags) of
+ select ->
+ ?SELECT(recvmsg, RecvRef);
+ Result ->
+ recvmsg_result(Result)
+ end.
+
+recvmsg_deadline(SockRef, BufSz, CtrlSz, Flags, Deadline) ->
+ RecvRef = make_ref(),
+ case prim_socket:recvmsg(SockRef, RecvRef, BufSz, CtrlSz, Flags) of
+ select ->
+ %% There is nothing just now, but we will be notified when there
+ %% is something to read (a select message).
+ Timeout = timeout(Deadline),
+ receive
+ ?mk_socket_msg(?mk_socket(SockRef), select, RecvRef) ->
+ recvmsg_deadline(
+ SockRef, BufSz, CtrlSz, Flags, Deadline);
+ ?mk_socket_msg(_Socket, abort, {RecvRef, Reason}) ->
+ {error, Reason}
+ after Timeout ->
+ cancel(SockRef, recvmsg, RecvRef),
+ {error, timeout}
+ end;
+ %%
+ {error, ealready = Reason} ->
+ %% Internal error:
+ %% we called recvmsg, got eagain, and called recvmsg again
+ %% - without waiting for select message
+ erlang:error(Reason);
+ Result ->
+ recvmsg_result(Result)
+ end.
+
+recvmsg_result(Result) ->
+ case Result of
+ {ok, _MsgHdr} = OK ->
+ OK;
+ {error, _Reason} = ERROR ->
+ ERROR
+ end.
+
+
+%% ===========================================================================
+%%
+%% close - close a file descriptor
+%%
+%% Closing a socket is a two stage rocket (because of linger).
+%% We need to perform the actual socket close while in BLOCKING mode.
+%% But that would hang the entire VM, so what we do is divide the
+%% close in two steps:
+%% 1) prim_socket:nif_close + the socket_stop (nif) callback function
+%% This is for everything that can be done safely NON-BLOCKING.
+%% 2) prim_socket:nif_finalize_close which is executed by a *dirty* scheduler
+%% Before we call the socket close function, we set the socket
+%% BLOCKING. Thereby linger is handled properly.
+
+-spec close(Socket) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Reason :: errcode() | closed | timeout.
+
+close(?mk_socket(SockRef))
+ when is_reference(SockRef) ->
+ case prim_socket:close(SockRef) of
+ ok ->
+ prim_socket:finalize_close(SockRef);
+ {ok, CloseRef} ->
+ %% We must wait for the socket_stop callback function to
+ %% complete its work
+ receive
+ ?mk_socket_msg(?mk_socket(SockRef), close, CloseRef) ->
+ prim_socket:finalize_close(SockRef)
+ end;
+ {error, _} = ERROR ->
+ ERROR
+ end;
+close(Socket) ->
+ erlang:error(badarg, [Socket]).
+
+
+
+%% ===========================================================================
+%%
+%% shutdown - shut down part of a full-duplex connection
+%%
+
+-spec shutdown(Socket, How) -> ok | {error, Reason} when
+ Socket :: socket(),
+ How :: shutdown_how(),
+ Reason :: inet:posix() | closed.
+
+shutdown(?mk_socket(SockRef), How)
+ when is_reference(SockRef) ->
+ prim_socket:shutdown(SockRef, How);
+shutdown(Socket, How) ->
+ erlang:error(badarg, [Socket, How]).
+
+
+%% ===========================================================================
+%%
+%% 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...
+%%
+%% <KOLLA>
+%%
+%% WE NEED TO MAKE SURE THAT THE USER DOES NOT MAKE US BLOCKING
+%% AS MUCH OF THE CODE EXPECTS TO BE NON-BLOCKING!!
+%%
+%% </KOLLA>
+
+-spec setopt(Socket, otp, otp_socket_option(), Value) ->
+ ok | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: errcode() | closed | not_owner;
+
+ (Socket, socket, socket_option(), Value) ->
+ ok | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: inet:posix() | closed;
+
+ (Socket, ip, ip_socket_option(), Value) ->
+ ok | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: inet:posix() | closed;
+
+ (Socket, ipv6, ipv6_socket_option(), Value) ->
+ ok | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: inet:posix() | closed;
+
+ (Socket, tcp, tcp_socket_option(), Value) ->
+ ok | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: inet:posix() | closed;
+
+ (Socket, udp, udp_socket_option(), Value) ->
+ ok | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: inet:posix() | closed;
+
+ (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 :: inet:posix() | closed.
+
+setopt(?mk_socket(SockRef), Level, Key, Value)
+ when is_reference(SockRef) ->
+ prim_socket:setopt(SockRef, Level, Key, Value);
+setopt(Socket, Level, Key, Value) ->
+ erlang:error(badarg, [Socket, Level, Key, Value]).
+
+
+%% ===========================================================================
+%%
+%% 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...
+%%
+%% 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, otp, otp_socket_option()) ->
+ {ok, Value} | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: einval | closed;
+
+ (Socket, socket, socket_option()) ->
+ {ok, Value} | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: inet:posix() | closed;
+
+ (Socket, ip, ip_socket_option()) ->
+ {ok, Value} | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: inet:posix() | closed;
+
+ (Socket, ipv6, ipv6_socket_option()) ->
+ {ok, Value} | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: inet:posix() | closed;
+
+ (Socket, tcp, tcp_socket_option()) ->
+ {ok, Value} | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: inet:posix() | closed;
+
+ (Socket, udp, udp_socket_option()) ->
+ {ok, Value} | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: inet:posix() | closed;
+
+ (Socket, sctp, sctp_socket_option()) ->
+ {ok, Value} | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: inet:posix() | closed;
+
+ (Socket, Level, Key) ->
+ ok | {ok, Value} | {error, Reason} when
+ Socket :: socket(),
+ Level :: integer(),
+ Key :: {NativeOpt, ValueSize},
+ NativeOpt :: integer(),
+ ValueSize :: int | bool | non_neg_integer(),
+ Value :: term(),
+ Reason :: inet:posix() | closed.
+
+getopt(?mk_socket(SockRef), Level, Key)
+ when is_reference(SockRef) ->
+ prim_socket:getopt(SockRef, Level, Key);
+getopt(Socket, Level, Key) ->
+ erlang:error(badarg, [Socket, Level, Key]).
+
+
+%% ===========================================================================
+%%
+%% sockname - return the current address of the socket.
+%%
+%%
+
+-spec sockname(Socket) -> {ok, SockAddr} | {error, Reason} when
+ Socket :: socket(),
+ SockAddr :: sockaddr(),
+ Reason :: inet:posix() | closed.
+
+sockname(?mk_socket(SockRef))
+ when is_reference(SockRef) ->
+ prim_socket:sockname(SockRef);
+sockname(Socket) ->
+ erlang:error(badarg, [Socket]).
+
+
+%% ===========================================================================
+%%
+%% peername - return the address of the peer *connected* to the socket.
+%%
+%%
+
+-spec peername(Socket) -> {ok, SockAddr} | {error, Reason} when
+ Socket :: socket(),
+ SockAddr :: sockaddr(),
+ Reason :: inet:posix() | closed.
+
+peername(?mk_socket(SockRef))
+ when is_reference(SockRef) ->
+ prim_socket:peername(SockRef);
+peername(Socket) ->
+ erlang:error(badarg, [Socket]).
+
+
+%% ===========================================================================
+%%
+%% cancel - cancel an operation resulting in a select
+%%
+%% A call to accept, recv/recvfrom/recvmsg and send/sendto/sendmsg
+%% can result in a select if they are called with the Timeout argument
+%% set to nowait. This is indicated by the return of the select-info.
+%% Such a operation can be cancelled by calling this function.
+%%
+
+-spec cancel(Socket, SelectInfo) -> ok | {error, Reason} when
+ Socket :: socket(),
+ SelectInfo :: select_info(),
+ Reason :: einval | closed | exself.
+
+cancel(?mk_socket(SockRef), ?SELECT_INFO(Tag, Ref))
+ when is_reference(SockRef) ->
+ cancel(SockRef, Tag, Ref);
+cancel(Socket, SelectInfo) ->
+ erlang:error(badarg, [Socket, SelectInfo]).
+
+
+cancel(SockRef, Op, OpRef) ->
+ case prim_socket:cancel(SockRef, Op, OpRef) of
+ %% The select has already completed
+ {error, select_sent} ->
+ flush_select_msg(SockRef, OpRef),
+ _ = flush_abort_msg(SockRef, OpRef),
+ ok;
+ {error, not_found} ->
+ _ = flush_abort_msg(SockRef, OpRef),
+ {error, einval};
+ Other ->
+ _ = flush_abort_msg(SockRef, OpRef),
+ Other
+ end.
+
+flush_select_msg(SockRef, Ref) ->
+ receive
+ ?mk_socket_msg(?mk_socket(SockRef), select, Ref) ->
+ ok
+ after 0 ->
+ ok
+ end.
+
+flush_abort_msg(SockRef, Ref) ->
+ receive
+ ?mk_socket_msg(?mk_socket(SockRef), abort, {Ref, Reason}) ->
+ Reason
+ after 0 ->
+ ok
+ end.
+
+
+%% ===========================================================================
+%%
+%% 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).
+
+
+deadline(Timeout) ->
+ case Timeout of
+ nowait ->
+ Timeout;
+ infinity ->
+ Timeout;
+ 0 ->
+ zero;
+ _ when is_integer(Timeout), 0 < Timeout ->
+ timestamp() + Timeout;
+ _ ->
+ badarg
+ end.
+
+timeout(Deadline) ->
+ case Deadline of
+ infinity ->
+ Deadline;
+ zero ->
+ 0;
+ _ ->
+ Now = timestamp(),
+ if
+ Deadline > Now ->
+ Deadline - Now;
+ true ->
+ 0
+ end
+ end.
+
+timestamp() ->
+ erlang:monotonic_time(milli_seconds).
+
+
+-compile({inline, [bincat/2]}).
+bincat(<<>>, <<_/binary>> = B) -> B;
+bincat(<<_/binary>> = A, <<>>) -> A;
+bincat(<<_/binary>> = A, <<_/binary>> = B) ->
+ <<A/binary, B/binary>>.
+
+
+%% p(F) ->
+%% p(F, []).
+
+%% p(F, A) ->
+%% p(get(sname), F, A).
+
+%% p(undefined, F, A) ->
+%% p("***", F, A);
+%% p(SName, F, A) ->
+%% TS = formated_timestamp(),
+%% io:format(user,"[~s][~s,~p] " ++ F ++ "~n", [TS, SName, self()|A]),
+%% io:format("[~s][~s,~p] " ++ F ++ "~n", [TS, SName, self()|A]).
diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile
index dcc892ec50..6120b29093 100644
--- a/lib/kernel/test/Makefile
+++ b/lib/kernel/test/Makefile
@@ -24,6 +24,21 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
# Target Specs
# ----------------------------------------------------
+SOCKET_MODULES = \
+ socket_test_lib \
+ socket_test_logger \
+ socket_test_evaluator \
+ socket_test_ttest_lib \
+ socket_test_ttest_tcp_gen \
+ socket_test_ttest_tcp_socket \
+ socket_test_ttest_tcp_client \
+ socket_test_ttest_tcp_client_gen \
+ socket_test_ttest_tcp_client_socket \
+ socket_test_ttest_tcp_server \
+ socket_test_ttest_tcp_server_gen \
+ socket_test_ttest_tcp_server_socket \
+ socket_SUITE
+
MODULES= \
erpc_SUITE \
rpc_SUITE \
@@ -89,6 +104,7 @@ MODULES= \
pg_SUITE \
pg2_SUITE \
seq_trace_SUITE \
+ $(SOCKET_MODULES) \
wrap_log_reader_SUITE \
cleanup \
ignore_cores \
@@ -113,6 +129,10 @@ APP_FILES = \
topApp3.app
ERL_FILES= $(MODULES:%=%.erl) code_a_test.erl
+HRL_FILES= \
+ socket_test_evaluator.hrl \
+ socket_test_ttest.hrl \
+ socket_test_ttest_client.hrl
TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
INSTALL_PROGS= $(TARGET_FILES)
@@ -172,7 +192,7 @@ release_spec: opt
release_tests_spec: make_emakefile
$(INSTALL_DIR) "$(RELSYSDIR)"
- $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)"
+ $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)"
$(INSTALL_DATA) $(APP_FILES) "$(RELSYSDIR)"
$(INSTALL_DATA) \
kernel.spec kernel_smoke.spec kernel_bench.spec logger.spec \
diff --git a/lib/kernel/test/net_SUITE.erl b/lib/kernel/test/net_SUITE.erl
index b3226f7e8c..5e6ee054f7 100644
--- a/lib/kernel/test/net_SUITE.erl
+++ b/lib/kernel/test/net_SUITE.erl
@@ -132,18 +132,13 @@ api_basic_cases() ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init_per_suite(Config) ->
- %% We test on the socket module for simplicity
- case lists:member(socket, erlang:loaded()) of
- true ->
- case os:type() of
- {win32, _} ->
- not_yet_implemented();
- _ ->
- %% ?LOGGER:start(),
- Config
- end;
- false ->
- {skip, "esock disabled"}
+ try net:info() of
+ #{} ->
+ %% ?LOGGER:start(),
+ Config
+ catch
+ error : notsup ->
+ {skip, "esock not supported"}
end.
end_per_suite(_) ->
diff --git a/erts/emulator/test/socket_SUITE.erl b/lib/kernel/test/socket_SUITE.erl
index 266175d947..906176cdf3 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/lib/kernel/test/socket_SUITE.erl
@@ -1906,24 +1906,20 @@ otp16359_cases() ->
init_per_suite(Config) ->
Factor = analyze_and_print_host_info(),
- case lists:member(socket, erlang:loaded()) of
- true ->
- case os:type() of
- {win32, _} ->
- (catch not_yet_implemented());
- _ ->
- case quiet_mode(Config) of
- default ->
- ?LOGGER:start(),
- [{esock_factor, Factor} | Config];
- Quiet ->
- ?LOGGER:start(Quiet),
- [{esock_factor, Factor},
- {esock_test_quiet, Quiet} | Config]
- end
- end;
- false ->
- {skip, "esock disabled"}
+ try socket:info() of
+ #{} ->
+ case quiet_mode(Config) of
+ default ->
+ ?LOGGER:start(),
+ [{esock_factor, Factor} | Config];
+ Quiet ->
+ ?LOGGER:start(Quiet),
+ [{esock_factor, Factor},
+ {esock_test_quiet, Quiet} | Config]
+ end
+ catch
+ error : notsup ->
+ {skip, "esock not supported"}
end.
end_per_suite(_) ->
@@ -2796,7 +2792,7 @@ api_b_open_and_close(InitState) ->
end},
#{desc => "get protocol",
cmd => fun(#{socket := Sock} = State) ->
- case socket:supports(options, socket, protocol) of
+ case socket:is_supported(options, socket, protocol) of
true ->
Res = socket:getopt(Sock, socket, protocol),
{ok, {State, Res}};
@@ -18926,7 +18922,7 @@ api_opt_ip_recvopts_udp(InitState) ->
#{desc => "test for ip:recvtos",
cmd => fun(State) ->
?SEV_IPRINT("test for ip:recvtos"),
- case socket:supports(options, ip, recvtos) of
+ case socket:is_supported(options, ip, recvtos) of
true ->
?SEV_IPRINT("use ip:recvtos"),
{ok, State#{recvtos => true}};
@@ -18938,7 +18934,7 @@ api_opt_ip_recvopts_udp(InitState) ->
#{desc => "test for socket:timestamp",
cmd => fun(State) ->
?SEV_IPRINT("test for socket:timestamp"),
- case socket:supports(options, socket, timestamp) of
+ case socket:is_supported(options, socket, timestamp) of
true ->
?SEV_IPRINT("use socket:timestamp"),
{ok, State#{timestamp => true}};
@@ -20884,25 +20880,25 @@ api_opt_ip_mopts_udp4(_Config) when is_list(_Config) ->
%% 'control message header type',
%% default | value()}]
Opts =
- case socket:supports(options, socket, timestamp) of
+ case socket:is_supported(options, socket, timestamp) of
true ->
[{socket, timestamp, timestamp, default}];
false ->
[]
end ++
- case socket:supports(options, ip, pktinfo) of
+ case socket:is_supported(options, ip, pktinfo) of
true ->
[{ip, pktinfo, pktinfo, default}];
false ->
[]
end ++
- case socket:supports(options, ip, recvorigdstaddr) of
+ case socket:is_supported(options, ip, recvorigdstaddr) of
true ->
[{ip, recvorigdstaddr, origdstaddr, default}];
false ->
[]
end ++
- case socket:supports(options, ip, recvtos) of
+ case socket:is_supported(options, ip, recvtos) of
true ->
%% It seems that sending any of the
%% TOS or TTL values will fail on:
@@ -20939,7 +20935,7 @@ api_opt_ip_mopts_udp4(_Config) when is_list(_Config) ->
{unix, darwin} ->
[];
_ ->
- case socket:supports(options, ip, recvttl) of
+ case socket:is_supported(options, ip, recvttl) of
true ->
%% It seems that sending any of the
%% TOS or TTL values will fail on:
@@ -21735,7 +21731,7 @@ api_opt_ipv6_hoplimit_udp6(_Config) when is_list(_Config) ->
end,
fun() ->
%% Begin by choosing which of the options we shall use
- Opt = case socket:supports(options, ipv6, recvhoplimit) of
+ Opt = case socket:is_supported(options, ipv6, recvhoplimit) of
true -> recvhoplimit;
false -> hoplimit
end,
@@ -22024,7 +22020,7 @@ api_opt_ipv6_tclass_udp6(_Config) when is_list(_Config) ->
end,
fun() ->
%% Begin by choosing which of the options we shall use
- Opt = case socket:supports(options, ipv6, recvtclass) of
+ Opt = case socket:is_supported(options, ipv6, recvtclass) of
true -> recvtclass;
false -> tclass
end,
@@ -22402,40 +22398,40 @@ api_opt_ipv6_mopts_udp6(_Config) when is_list(_Config) ->
%% control message header type(s):
%% [{'ipv6 socket option', 'control message header type'}]
Opts =
- case socket:supports(options, socket, timestamp) of
+ case socket:is_supported(options, socket, timestamp) of
true ->
[{socket, timestamp, timestamp, default}];
false ->
[]
end ++
- case socket:supports(options, ipv6, recvpktinfo) of
+ case socket:is_supported(options, ipv6, recvpktinfo) of
true ->
[{ipv6, recvpktinfo, pktinfo, default}];
false ->
[]
end ++
- case socket:supports(options, ipv6, flowinfo) of
+ case socket:is_supported(options, ipv6, flowinfo) of
true ->
[{ipv6, flowinfo, flowinfo, default}];
false ->
[]
end ++
- case socket:supports(options, ipv6, recvhoplimit) of
+ case socket:is_supported(options, ipv6, recvhoplimit) of
true ->
[{ipv6, recvhoplimit, hoplimit, default}];
false ->
- case socket:supports(options, ipv6, hoplimit) of
+ case socket:is_supported(options, ipv6, hoplimit) of
true ->
[{ipv6, hoplimit, hoplimit, default}];
false ->
[]
end
end ++
- case socket:supports(options, ipv6, recvtclass) of
+ case socket:is_supported(options, ipv6, recvtclass) of
true ->
[{ipv6, recvtclass, tclass, 42}];
false ->
- case socket:supports(options, ipv6, tclass) of
+ case socket:is_supported(options, ipv6, tclass) of
true ->
[{ipv6, tclass, tclass, 42}];
false ->
@@ -24534,7 +24530,7 @@ api_to_recv_tcp6(_Config) when is_list(_Config) ->
tc_try(api_to_recv_tcp6,
fun() -> has_support_ipv6() end,
fun() ->
- case socket:supports(ipv6) of
+ case socket:is_supported(ipv6) of
true ->
Recv = fun(Sock, To) ->
socket:recv(Sock, 0, To)
@@ -43074,8 +43070,8 @@ has_support_ip_recvtos() ->
has_support_socket_option_ip(recvtos).
has_support_ip_recvtos_and_or_sock_timestamp() ->
- case (socket:supports(options, ip, recvtos) orelse
- socket:supports(options, socket, timestamp)) of
+ case (socket:is_supported(options, ip, recvtos) orelse
+ socket:is_supported(options, socket, timestamp)) of
true ->
ok;
false ->
@@ -43099,8 +43095,8 @@ has_support_ipv6_flowinfo() ->
has_support_socket_option_ipv6(flowinfo).
has_support_ipv6_hoplimit_or_recvhoplimit() ->
- %% case (socket:supports(options, ipv6, recvhoplimit) orelse
- %% socket:supports(options, ipv6, hoplimit)) of
+ %% case (socket:is_supported(options, ipv6, recvhoplimit) orelse
+ %% socket:is_supported(options, ipv6, hoplimit)) of
case is_any_options_supported([{ipv6, recvhoplimit}, {ipv6, hoplimit}]) of
true ->
ok;
@@ -43165,7 +43161,7 @@ has_support_socket_option_udp(Opt) ->
has_support_socket_option(Level, Option) ->
- case socket:supports(options, Level, Option) of
+ case socket:is_supported(options, Level, Option) of
true ->
ok;
false ->
@@ -43173,7 +43169,7 @@ has_support_socket_option(Level, Option) ->
end.
is_any_options_supported(Options) ->
- Pred = fun({Level, Option}) -> socket:supports(options, Level, Option) end,
+ Pred = fun({Level, Option}) -> socket:is_supported(options, Level, Option) end,
lists:any(Pred, Options).
@@ -43195,7 +43191,7 @@ has_support_recv_flag(Flag) ->
has_support_send_or_recv_flag("Recv", recv_flags, Flag).
has_support_send_or_recv_flag(Pre, Key, Flag) ->
- case socket:supports(Key, Flag) of
+ case socket:is_supported(Key, Flag) of
true ->
ok;
false ->
@@ -43260,7 +43256,7 @@ has_support_unix_domain_socket() ->
{win32, _} ->
skip("Not supported");
_ ->
- case socket:supports(local) of
+ case socket:is_supported(local) of
true ->
ok;
false ->
@@ -43273,7 +43269,7 @@ has_support_sctp() ->
{win32, _} ->
skip("Not supported");
_ ->
- case socket:supports(sctp) of
+ case socket:is_supported(sctp) of
true ->
ok;
false ->
diff --git a/erts/emulator/test/socket_test_evaluator.erl b/lib/kernel/test/socket_test_evaluator.erl
index 694f0d5f1e..694f0d5f1e 100644
--- a/erts/emulator/test/socket_test_evaluator.erl
+++ b/lib/kernel/test/socket_test_evaluator.erl
diff --git a/erts/emulator/test/socket_test_evaluator.hrl b/lib/kernel/test/socket_test_evaluator.hrl
index 5be49dc022..5be49dc022 100644
--- a/erts/emulator/test/socket_test_evaluator.hrl
+++ b/lib/kernel/test/socket_test_evaluator.hrl
diff --git a/erts/emulator/test/socket_test_lib.erl b/lib/kernel/test/socket_test_lib.erl
index 6440788651..1759f81af3 100644
--- a/erts/emulator/test/socket_test_lib.erl
+++ b/lib/kernel/test/socket_test_lib.erl
@@ -100,7 +100,7 @@ f(F, A) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
has_support_ipv6() ->
- case socket:supports(ipv6) of
+ case socket:is_supported(ipv6) of
true ->
ok;
false ->
diff --git a/erts/emulator/test/socket_test_logger.erl b/lib/kernel/test/socket_test_logger.erl
index f5d4c8c7b2..f5d4c8c7b2 100644
--- a/erts/emulator/test/socket_test_logger.erl
+++ b/lib/kernel/test/socket_test_logger.erl
diff --git a/erts/emulator/test/socket_test_ttest.hrl b/lib/kernel/test/socket_test_ttest.hrl
index 1a004a9a7a..1a004a9a7a 100644
--- a/erts/emulator/test/socket_test_ttest.hrl
+++ b/lib/kernel/test/socket_test_ttest.hrl
diff --git a/erts/emulator/test/socket_test_ttest_client.hrl b/lib/kernel/test/socket_test_ttest_client.hrl
index 84e736cc34..84e736cc34 100644
--- a/erts/emulator/test/socket_test_ttest_client.hrl
+++ b/lib/kernel/test/socket_test_ttest_client.hrl
diff --git a/erts/emulator/test/socket_test_ttest_lib.erl b/lib/kernel/test/socket_test_ttest_lib.erl
index ebce16dcfa..ebce16dcfa 100644
--- a/erts/emulator/test/socket_test_ttest_lib.erl
+++ b/lib/kernel/test/socket_test_ttest_lib.erl
diff --git a/erts/emulator/test/socket_test_ttest_tcp_client.erl b/lib/kernel/test/socket_test_ttest_tcp_client.erl
index f28819ca69..f28819ca69 100644
--- a/erts/emulator/test/socket_test_ttest_tcp_client.erl
+++ b/lib/kernel/test/socket_test_ttest_tcp_client.erl
diff --git a/erts/emulator/test/socket_test_ttest_tcp_client_gen.erl b/lib/kernel/test/socket_test_ttest_tcp_client_gen.erl
index 65a3a94d38..65a3a94d38 100644
--- a/erts/emulator/test/socket_test_ttest_tcp_client_gen.erl
+++ b/lib/kernel/test/socket_test_ttest_tcp_client_gen.erl
diff --git a/erts/emulator/test/socket_test_ttest_tcp_client_socket.erl b/lib/kernel/test/socket_test_ttest_tcp_client_socket.erl
index 2fb1242028..2fb1242028 100644
--- a/erts/emulator/test/socket_test_ttest_tcp_client_socket.erl
+++ b/lib/kernel/test/socket_test_ttest_tcp_client_socket.erl
diff --git a/erts/emulator/test/socket_test_ttest_tcp_gen.erl b/lib/kernel/test/socket_test_ttest_tcp_gen.erl
index 5d20e49359..5d20e49359 100644
--- a/erts/emulator/test/socket_test_ttest_tcp_gen.erl
+++ b/lib/kernel/test/socket_test_ttest_tcp_gen.erl
diff --git a/erts/emulator/test/socket_test_ttest_tcp_server.erl b/lib/kernel/test/socket_test_ttest_tcp_server.erl
index 2394dc7924..2394dc7924 100644
--- a/erts/emulator/test/socket_test_ttest_tcp_server.erl
+++ b/lib/kernel/test/socket_test_ttest_tcp_server.erl
diff --git a/erts/emulator/test/socket_test_ttest_tcp_server_gen.erl b/lib/kernel/test/socket_test_ttest_tcp_server_gen.erl
index fdf40f1369..fdf40f1369 100644
--- a/erts/emulator/test/socket_test_ttest_tcp_server_gen.erl
+++ b/lib/kernel/test/socket_test_ttest_tcp_server_gen.erl
diff --git a/erts/emulator/test/socket_test_ttest_tcp_server_socket.erl b/lib/kernel/test/socket_test_ttest_tcp_server_socket.erl
index 4045bf4e4e..4045bf4e4e 100644
--- a/erts/emulator/test/socket_test_ttest_tcp_server_socket.erl
+++ b/lib/kernel/test/socket_test_ttest_tcp_server_socket.erl
diff --git a/erts/emulator/test/socket_test_ttest_tcp_socket.erl b/lib/kernel/test/socket_test_ttest_tcp_socket.erl
index a1e08e605c..a1e08e605c 100644
--- a/erts/emulator/test/socket_test_ttest_tcp_socket.erl
+++ b/lib/kernel/test/socket_test_ttest_tcp_socket.erl
diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl
index 1565366d92..61a39884f3 100644
--- a/lib/sasl/src/systools_make.erl
+++ b/lib/sasl/src/systools_make.erl
@@ -47,11 +47,9 @@
-compile({inline,[{badarg,2}]}).
-ifdef(USE_ESOCK).
--define(ESOCK_SOCKET_MODS, [socket, socket_registry]).
--define(ESOCK_NET_MODS, [prim_net]).
+-define(ESOCK_MODS, [prim_net,prim_socket,socket_registry]).
-else.
--define(ESOCK_SOCKET_MODS, []).
--define(ESOCK_NET_MODS, []).
+-define(ESOCK_MODS, []).
-endif.
@@ -1578,12 +1576,13 @@ mandatory_modules() ->
%% This is the modules that are preloaded into the Erlang system.
preloaded() ->
- %% Sorted
- [atomics,counters,erl_init,erl_prim_loader,erl_tracer,erlang,
- erts_code_purger,erts_dirty_process_signal_handler,
- erts_internal,erts_literal_area_collector,
- init,persistent_term,prim_buffer,prim_eval,prim_file,
- prim_inet] ++ ?ESOCK_NET_MODS ++ [prim_zip] ++ ?ESOCK_SOCKET_MODS ++ [zlib].
+ lists:sort(
+ ?ESOCK_MODS ++
+ [atomics,counters,erl_init,erl_prim_loader,erl_tracer,erlang,
+ erts_code_purger,erts_dirty_process_signal_handler,
+ erts_internal,erts_literal_area_collector,
+ init,persistent_term,prim_buffer,prim_eval,prim_file,
+ prim_inet,prim_zip,zlib]).
%%______________________________________________________________________
%% Kernel processes; processes that are specially treated by the init
diff --git a/lib/snmp/test/snmp_test_lib.erl b/lib/snmp/test/snmp_test_lib.erl
index 3ae1bc792c..0bd2b87591 100644
--- a/lib/snmp/test/snmp_test_lib.erl
+++ b/lib/snmp/test/snmp_test_lib.erl
@@ -507,7 +507,7 @@ has_support_ipv6() ->
%% so for windows we need to use the old style...
old_has_support_ipv6();
_ ->
- socket:supports(ipv6) andalso has_valid_ipv6_address()
+ socket:is_supported(ipv6) andalso has_valid_ipv6_address()
end.
has_valid_ipv6_address() ->