summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaimo Niskanen <raimo@erlang.org>2020-04-01 15:16:54 +0200
committerRaimo Niskanen <raimo@erlang.org>2020-04-01 15:54:14 +0200
commit54ae202df1511fe611d682f128970505cd0393f4 (patch)
tree9eae6c670788f0cc1cf74fbb8574b115252289b7
parent54aad59898321ae57b282519268ebff895beb37d (diff)
downloaderlang-54ae202df1511fe611d682f128970505cd0393f4.tar.gz
Split socket module
Split into preloaded prim_socket.erl, NIF prim_socket_nif.erl, and socket.erl in kernel.
-rw-r--r--erts/doc/src/Makefile30
-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)811
-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 -> 31976 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/prim_net.erl17
-rw-r--r--erts/preloaded/src/prim_socket.erl1390
-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)0
-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/net.erl22
-rw-r--r--lib/kernel/src/socket.erl2445
-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
44 files changed, 4385 insertions, 4704 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/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..93cca127ae 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) \
{ \
@@ -1139,26 +1202,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 +1239,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);
@@ -3181,6 +3248,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 +3263,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); \
@@ -3418,8 +3488,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),
@@ -3882,101 +3958,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 +5272,7 @@ ERL_NIF_TERM esock_supports_options_sctp(ErlNifEnv* env)
#endif
+#if 0
#if !defined(__WIN32__)
static
@@ -5228,6 +5345,7 @@ ERL_NIF_TERM esock_supports_netns(ErlNifEnv* env)
}
#endif
+#endif // #if 0
#if !defined(__WIN32__)
@@ -5432,7 +5550,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 +5596,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 +5633,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 +5642,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"
@@ -5539,7 +5660,7 @@ ERL_NIF_TERM esock_open2(ErlNifEnv* env,
*/
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)) {
@@ -5549,7 +5670,7 @@ ERL_NIF_TERM esock_open2(ErlNifEnv* env,
}
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);
@@ -5559,7 +5680,7 @@ ERL_NIF_TERM esock_open2(ErlNifEnv* env,
}
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);
@@ -5568,7 +5689,7 @@ ERL_NIF_TERM esock_open2(ErlNifEnv* env,
}
- SGDBG2( dbg,
+ SSDBG2( dbg,
("SOCKET", "esock_open2 -> "
"\r\n domain: %d"
"\r\n type: %d"
@@ -5581,7 +5702,7 @@ ERL_NIF_TERM esock_open2(ErlNifEnv* env,
if (IS_SOCKET_ERROR(sock = dup(fd))) {
save_errno = sock_errno();
- SGDBG2( dbg,
+ SSDBG2( dbg,
("SOCKET", "esock_open2 -> dup failed: %d\r\n", save_errno) );
/* reason = {dup, 'errno atom'} */
@@ -5617,10 +5738,10 @@ ERL_NIF_TERM esock_open2(ErlNifEnv* env,
int code = sock_peer(descP->sock,
(struct sockaddr*) &descP->remote, &sz);
if (! IS_SOCKET_ERROR(code)) {
- SGDBG2( dbg, ("SOCKET", "esock_open2 -> connected\r\n") );
+ SSDBG2( dbg, ("SOCKET", "esock_open2 -> connected\r\n") );
descP->writeState = ESOCK_STATE_CONNECTED;
} else {
- SGDBG2( dbg, ("SOCKET", "esock_open2 -> not connected\r\n") );
+ SSDBG2( dbg, ("SOCKET", "esock_open2 -> not connected\r\n") );
}
}
@@ -5643,6 +5764,7 @@ ERL_NIF_TERM esock_open2(ErlNifEnv* env,
return esock_make_error(env, atom_exmon);
+ descP->dbg = dbg;
inc_socket(domain, type, protocol);
/* And finally update the registry.
@@ -5650,23 +5772,13 @@ 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
@@ -5769,7 +5881,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 +5892,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 +5923,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 +5969,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);
@@ -5900,10 +6012,8 @@ ERL_NIF_TERM esock_open4(ErlNifEnv* env,
}
- 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 +6022,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)
{
@@ -20132,93 +20252,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 +20328,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)
@@ -21952,7 +22035,9 @@ 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
@@ -22105,4 +22190,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..df22e206de
--- /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..ce35e97e4f 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/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..8fdf898f27
--- /dev/null
+++ b/erts/preloaded/src/prim_socket.erl
@@ -0,0 +1,1390 @@
+%%
+%% %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/2, finalize_connection/1,
+ 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(FD, Opts) ->
+ erlang:error(badarg, [FD, Opts]).
+
+open(Domain, Type, Protocol, Opts) when is_map(Opts) ->
+ try
+ begin
+ 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)
+ end
+ catch
+ throw:ERROR ->
+ ERROR
+ end;
+open(Domain, Type, Protocol, Opts) ->
+ erlang:error(badarg, [Domain, Type, Protocol, Opts]).
+
+%% ----------------------------------
+
+bind(SockRef, Addr) ->
+ try nif_bind(SockRef, enc_sockaddr(Addr))
+ catch
+ throw:ERROR ->
+ ERROR
+ end.
+
+bind(SockRef, Addrs, Action) when is_list(Addrs) ->
+ try
+ EAddrs = [enc_sockaddr(Addr) || Addr <- Addrs],
+ nif_bind(SockRef, EAddrs, Action)
+ catch
+ throw:ERROR ->
+ ERROR
+ end;
+bind(SockRef, Addrs, Action) ->
+ erlang:error(badarg, [SockRef, Addrs, Action]).
+
+%% ----------------------------------
+
+connect(SockRef, SockAddr) ->
+ try nif_connect(SockRef, enc_sockaddr(SockAddr)) of
+ ok ->
+ ok;
+ {ok, Ref} ->
+ {select, Ref};
+ {error, _} = Error ->
+ Error
+ catch
+ throw:ERROR ->
+ ERROR
+ end.
+
+finalize_connection(SockRef) ->
+ nif_finalize_connection(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) ->
+ try
+ EFlags = enc_send_flags(Flags),
+ send_result(
+ nif_send(SockRef, SendRef, Data, EFlags))
+ catch
+ throw:ERROR ->
+ ERROR
+ end.
+
+sendto(SockRef, SendRef, Data, To, Flags) ->
+ try
+ ETo = enc_sockaddr(To),
+ EFlags = enc_send_flags(Flags),
+ send_result(
+ nif_sendto(SockRef, SendRef, Data, ETo, EFlags))
+ catch
+ throw:ERROR ->
+ ERROR
+ end.
+
+sendmsg(SockRef, SendRef, MsgHdr, Flags) ->
+ try
+ EMsgHdr = enc_msghdr(MsgHdr),
+ EFlags = enc_send_flags(Flags),
+ send_result(
+ nif_sendmsg(SockRef, SendRef, EMsgHdr, EFlags))
+ catch
+ throw:ERROR ->
+ ERROR
+ end.
+
+
+send_result(Result) ->
+ case Result of
+ ok -> ok;
+ {ok, _Written} = OK -> OK;
+ {error, eagain} -> select;
+ {error, _} = ERROR -> ERROR
+ end.
+
+%% ----------------------------------
+
+recv(SockRef, RecvRef, Length, Flags) ->
+ try
+ EFlags = enc_recv_flags(Flags),
+ 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
+ catch
+ throw:ERROR ->
+ ERROR
+ end.
+
+recvfrom(SockRef, RecvRef, Length, Flags) ->
+ try
+ EFlags = enc_recv_flags(Flags),
+ recvfromsg_result(
+ nif_recvfrom(SockRef, RecvRef, Length, EFlags))
+ catch
+ throw:ERROR ->
+ ERROR
+ end.
+
+recvmsg(SockRef, RecvRef, BufSz, CtrlSz, Flags) ->
+ try
+ EFlags = enc_recv_flags(Flags),
+ recvfromsg_result(
+ nif_recvmsg(SockRef, RecvRef, BufSz, CtrlSz, EFlags))
+ catch
+ throw:ERROR ->
+ ERROR
+ end.
+
+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) ->
+ try
+ EHow = enc_shutdown_how(How),
+ nif_shutdown(SockRef, EHow)
+ catch
+ throw:ERROR ->
+ ERROR
+ end.
+
+%% ----------------------------------
+
+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) ->
+ try
+ {Type, ELevel, EKey} = enc_sockopt_type(Level, Key),
+ EValue = enc_setopt_value(Level, Key, Value, Type),
+ nif_setopt(SockRef, true, ELevel, EKey, EValue)
+ catch
+ throw:ERROR ->
+ ERROR
+ end.
+
+getopt(SockRef, Level, Key)
+ when is_integer(Level), is_integer(Key) ->
+ nif_getopt(SockRef, false, Level, Key);
+getopt(SockRef, Level, Key) ->
+ try
+ {_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
+ catch
+ throw: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;
+ _ -> err({unknown_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;
+ _ -> err({unknown_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(#{}) ->
+ einval().
+
+
+%% Common to setopt and getopt
+%% - the returned first tuple element is a type tag
+%% that is used by setopt and ignored by getopt
+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 -> {get, L, ?ESOCK_OPT_OTP_FD};
+ meta -> {term, L, ?ESOCK_OPT_OTP_META};
+ domain -> {get, 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 -> {get, L, ?ESOCK_OPT_SOCK_ACCEPTCONN};
+ acceptfilter -> {not_supported, 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 -> {not_supported, L, ?ESOCK_OPT_SOCK_BUSY_POLL};
+ debug -> {integer, L, ?ESOCK_OPT_SOCK_DEBUG};
+ domain -> {get, L, ?ESOCK_OPT_SOCK_DOMAIN};
+ dontroute -> {boolean, L, ?ESOCK_OPT_SOCK_DONTROUTE};
+ error -> {not_supported, L, ?ESOCK_OPT_SOCK_ERROR};
+ keepalive -> {boolean, L, ?ESOCK_OPT_SOCK_KEEPALIVE};
+ linger -> {linger, L, ?ESOCK_OPT_SOCK_LINGER};
+ mark -> {not_supported, 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 -> {not_supported, L, ?ESOCK_OPT_SOCK_PEERCRED};
+ priority -> {integer, L, ?ESOCK_OPT_SOCK_PRIORITY};
+ protocol -> {get, L, ?ESOCK_OPT_SOCK_PROTOCOL};
+ rcvbuf -> {integer, L, ?ESOCK_OPT_SOCK_RCVBUF};
+ rcvbufforce -> {not_supported, 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 -> {not_supported, L, ?ESOCK_OPT_SOCK_RXQ_OVFL};
+ setfib -> {not_supported, L, ?ESOCK_OPT_SOCK_SETFIB};
+ sndbuf -> {integer, L, ?ESOCK_OPT_SOCK_SNDBUF};
+ sndbufforce -> {not_supported, 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 -> {get, 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 -> {get, 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 ->
+ {not_supported, 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 ->
+ {not_supported, L, ?ESOCK_OPT_IPV6_AUTH_LEVEL};
+ checksum ->
+ {not_supported, 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 ->
+ {not_supported, L, ?ESOCK_OPT_IPV6_ESP_NETWORK_LEVEL};
+ esp_trans_level ->
+ {not_supported, 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 ->
+ {not_supported, L, ?ESOCK_OPT_IPV6_IPCOMP_LEVEL};
+ join_group ->
+ {not_supported, L, ?ESOCK_OPT_IPV6_JOIN_GROUP};
+ leave_group ->
+ {not_supported, 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 ->
+ {not_supported, L, ?ESOCK_OPT_IPV6_PORTRANGE};
+ pktoptions ->
+ {not_supported, 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 ->
+ {not_supported, 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 ->
+ {not_supported, L, ?ESOCK_OPT_TCP_INFO};
+ keepcnt ->
+ {not_supported, L, ?ESOCK_OPT_TCP_KEEPCNT};
+ keepidle ->
+ {not_supported, L, ?ESOCK_OPT_TCP_KEEPIDLE};
+ keepintvl ->
+ {not_supported, L, ?ESOCK_OPT_TCP_KEEPINTVL};
+ maxseg ->
+ {integer, L, ?ESOCK_OPT_TCP_MAXSEG};
+ md5seg ->
+ {not_supported, L, ?ESOCK_OPT_TCP_MD5SIG};
+ nodelay ->
+ {boolean, L, ?ESOCK_OPT_TCP_NODELAY};
+ noopt ->
+ {not_supported, L, ?ESOCK_OPT_TCP_NOOPT};
+ nopush ->
+ {not_supported, L, ?ESOCK_OPT_TCP_NOPUSH};
+ syncnt ->
+ %% Only set? 1..255
+ {not_supported, L, ?ESOCK_OPT_TCP_SYNCNT};
+ user_timeout ->
+ {not_supported, 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 ->
+ {not_supported, L, ?ESOCK_OPT_SCTP_ADAPTION_LAYER};
+ associnfo -> {Key, L, ?ESOCK_OPT_SCTP_ASSOCINFO};
+ auth_active_key ->
+ {not_supported, L, ?ESOCK_OPT_SCTP_AUTH_ACTIVE_KEY};
+ auth_asconf ->
+ {not_supported, L, ?ESOCK_OPT_SCTP_AUTH_ASCONF};
+ auth_chunk ->
+ {not_supported, L, ?ESOCK_OPT_SCTP_AUTH_CHUNK};
+ auth_key ->
+ {not_supported, L, ?ESOCK_OPT_SCTP_AUTH_KEY};
+ auth_delete_key ->
+ {not_supported, L, ?ESOCK_OPT_SCTP_AUTH_DELETE_KEY};
+ autoclose ->
+ {integer, L, ?ESOCK_OPT_SCTP_AUTOCLOSE};
+ context ->
+ {not_supported, L, ?ESOCK_OPT_SCTP_CONTEXT};
+ default_send_params ->
+ {not_supported, L, ?ESOCK_OPT_SCTP_DEFAULT_SEND_PARAMS};
+ delayed_ack_time ->
+ {not_supported, L, ?ESOCK_OPT_SCTP_DELAYED_ACK_TIME};
+ disable_fragments ->
+ {boolean, L, ?ESOCK_OPT_SCTP_DISABLE_FRAGMENTS};
+ hmac_ident ->
+ {not_supported, L, ?ESOCK_OPT_SCTP_HMAC_IDENT};
+ events -> {Key, L, ?ESOCK_OPT_SCTP_EVENTS};
+ explicit_eor ->
+ {not_supported, L, ?ESOCK_OPT_SCTP_EXPLICIT_EOR};
+ fragment_intreleave ->
+ {not_supported, L, ?ESOCK_OPT_SCTP_FRAGMENT_INTERLEAVE};
+ get_peer_addr_info ->
+ {not_supported, L, ?ESOCK_OPT_SCTP_GET_PEER_ADDR_INFO};
+ initmsg -> {Key, L, ?ESOCK_OPT_SCTP_INITMSG};
+ i_want_mapped_v4_addr ->
+ {not_supported, L, ?ESOCK_OPT_SCTP_I_WANT_MAPPED_V4_ADDR};
+ local_auth_chunks ->
+ {not_supported, L, ?ESOCK_OPT_SCTP_LOCAL_AUTH_CHUNKS};
+ maxseg ->
+ {integer, L, ?ESOCK_OPT_SCTP_MAXSEG};
+ maxburst ->
+ {not_supported, L, ?ESOCK_OPT_SCTP_MAXBURST};
+ nodelay ->
+ {boolean, L, ?ESOCK_OPT_SCTP_NODELAY};
+ partial_delivery_point ->
+ {not_supported, L, ?ESOCK_OPT_SCTP_PARTIAL_DELIVERY_POINT};
+ peer_addr_params ->
+ {not_supported, L, ?ESOCK_OPT_SCTP_PEER_ADDR_PARAMS};
+ peer_auth_chunks ->
+ {not_supported, L, ?ESOCK_OPT_SCTP_PEER_AUTH_CHUNKS};
+ primary_addr ->
+ {not_supported, L, ?ESOCK_OPT_SCTP_PRIMARY_ADDR};
+ reset_streams ->
+ {not_supported, L, ?ESOCK_OPT_SCTP_RESET_STREAMS};
+ rtoinfo -> {Key, L, ?ESOCK_OPT_SCTP_RTOINFO};
+ set_peer_primary_addr ->
+ {not_supported, L, ?ESOCK_OPT_SCTP_SET_PEER_PRIMARY_ADDR};
+ status -> % ?ESOCK_OPT_SCTP_RTOINFO;
+ {not_supported, L, ?ESOCK_OPT_SCTP_STATUS};
+ use_ext_recvinfo ->
+ {not_supported, 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;
+ {_, Value} ->
+ 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;
+ _ -> err({unknown_flag, 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, invalid_domain/1}).
+invalid_domain(Domain) ->
+ err({invalid_domain, Domain}).
+
+-dialyzer({no_return, invalid_type/1}).
+invalid_type(Type) ->
+ err({invalid_type, Type}).
+
+-dialyzer({no_return, invalid_protocol/1}).
+invalid_protocol(Proto) ->
+ err({invalid_protocol, Proto}).
+
+-dialyzer({no_return, invalid_address/1}).
+invalid_address(SockAddr) ->
+ err({invalid_address, SockAddr}).
+
+-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) ->
+ err({not_supported, What}).
+
+-dialyzer({no_return, einval/0}).
+einval() ->
+ err(einval).
+
+err(Reason) ->
+ throw({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, _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_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 9b004b3781..7d1d843d4f 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
diff --git a/lib/kernel/doc/src/part.xml b/lib/kernel/doc/src/part.xml
index 7a7a92b067..7ecd8cbffa 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"/>
</part>
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..53cf9ed0c8 100644
--- a/erts/doc/src/socket.xml
+++ b/lib/kernel/doc/src/socket.xml
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/net.erl b/lib/kernel/src/net.erl
index aceb903bb6..82ff4747dc 100644
--- a/lib/kernel/src/net.erl
+++ b/lib/kernel/src/net.erl
@@ -226,25 +226,15 @@ 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)) ->
+getnameinfo(SockAddr, Flags)
+ when is_map(SockAddr), is_list(Flags);
+ is_map(SockAddr), Flags =:= undefined ->
erlang:error(notsup).
-endif.
diff --git a/lib/kernel/src/socket.erl b/lib/kernel/src/socket.erl
new file mode 100644
index 0000000000..65284204a3
--- /dev/null
+++ b/lib/kernel/src/socket.erl
@@ -0,0 +1,2445 @@
+%%
+%% %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/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().
+
+-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()}.
+
+%% ===========================================================================
+%%
+%% Interface term formats
+%%
+
+-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').
+
+
+%% ===========================================================================
+%%
+%% 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(#socket{ref = SockRef}) ->
+ prim_socket: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() ->
+ [{sctp, boolean()} |
+ {ipv6, boolean()} |
+ {local, boolean()} |
+ {netns, boolean()} |
+ {send_flags, supports_send_flags()} |
+ {recv_flags, supports_recv_flags()} |
+ {options, supports_options()}].
+%%
+supports() ->
+ [{Key1, supports(Key1)}
+ || Key1 <- [options, send_flags, recv_flags]]
+ ++ prim_socket:supports().
+
+-spec supports(options) -> supports_options();
+ (send_flags) -> supports_send_flags();
+ (recv_flags) -> supports_recv_flags();
+ (Key1 :: term()) -> [].
+%%
+supports(options) ->
+ [{Level, supports(options, Level)}
+ || Level <- [socket, ip, ipv6, tcp, udp, sctp]];
+supports(Key) ->
+ prim_socket:supports(Key).
+
+-spec supports(options, socket) -> supports_options_socket();
+ (options, ip) -> supports_options_ip();
+ (options, ipv6) -> supports_options_ipv6();
+ (options, tcp) -> supports_options_tcp();
+ (options, udp) -> supports_options_udp();
+ (options, sctp) -> supports_options_sctp();
+ (Key1 :: term(), Key2 :: term()) -> [].
+%%
+supports(Key1, Key2) ->
+ prim_socket:supports(Key1, Key2).
+
+
+-spec is_supported(sctp | ipv6 | local | netns) -> boolean();
+ (Key :: term()) -> false.
+is_supported(Key) ->
+ get_is_supported(Key, supports()).
+
+-spec is_supported(send_flags, send_flag()) -> boolean();
+ (recv_flags, recv_flag()) -> boolean();
+ (Key1 :: term(), Key2 :: term()) -> false.
+is_supported(Key1, Key2) ->
+ get_is_supported(Key2, supports(Key1)).
+
+-spec is_supported(options, socket, socket_option()) -> boolean();
+ (options, ip, ip_socket_option()) -> boolean();
+ (options, ipv6, ipv6_socket_option()) -> boolean();
+ (options, tcp, tcp_socket_option()) -> boolean();
+ (options, udp, udp_socket_option()) -> boolean();
+ (options, sctp, sctp_socket_option()) -> boolean();
+ (Key1 :: term(), Key2 :: term(), Key3 :: term()) ->
+ false.
+%%
+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 :: term().
+
+open(FD) ->
+ open(FD, #{}).
+
+-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), is_map(Opts) ->
+ case prim_socket:open(FD, 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, #{}).
+
+-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) ->
+ case prim_socket:open(Domain, Type, Protocol, Opts) of
+ {ok, SockRef} ->
+ Socket = #socket{ref = 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 :: any | broadcast | loopback | sockaddr(),
+ Port :: port_number(),
+ Reason :: term().
+
+bind(#socket{ref = SockRef}, Addr)
+ when 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, {invalid_domain, Domain}};
+ {error, _} = ERROR ->
+ ERROR
+ end;
+bind(#socket{ref = SockRef}, Addr)
+ when is_map(Addr) ->
+ prim_socket:bind(SockRef, Addr);
+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 :: term().
+
+bind(#socket{ref = SockRef}, Addrs, Action)
+ when is_list(Addrs), Action =:= add;
+ is_list(Addrs), 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, 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} = Socket, SockAddr, Timeout) ->
+ case deadline(Timeout) of
+ badarg = Reason ->
+ erlang:error(Reason, [Socket, SockAddr, Timeout]);
+ Deadline ->
+ do_connect(SockRef, SockAddr, Deadline)
+ end;
+connect(Socket, SockAddr, Timeout) ->
+ erlang:error(badarg, [Socket, SockAddr, Timeout]).
+
+do_connect(SockRef, SockAddr, Deadline) ->
+
+ case prim_socket:connect(SockRef, SockAddr) of
+
+ ok ->
+ %% Connected!
+ ok;
+
+
+ {select, Ref} when (Deadline =:= nowait) ->
+ %% Connecting, but the caller does not want to wait...
+ ?SELECT(connect, Ref);
+
+ {select, Ref} ->
+ %% Connecting...
+ T = timeout(Deadline),
+ receive
+ {?ESOCK_TAG, _Socket, select, Ref} ->
+ prim_socket:finalize_connection(SockRef);
+
+ {?ESOCK_TAG, _Socket, abort, {Ref, Reason}} ->
+ {error, Reason}
+
+ after T ->
+ 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 :: integer(),
+ Reason :: term().
+
+listen(#socket{ref = SockRef}, Backlog)
+ when 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 :: 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} = Socket, Timeout) ->
+ case deadline(Timeout) of
+ badarg = Reason ->
+ erlang:error(Reason, [Socket, Timeout]);
+ Deadline ->
+ do_accept(LSockRef, Deadline)
+ end;
+accept(Socket, Timeout) ->
+ erlang:error(badarg, [Socket, Timeout]).
+
+
+do_accept(LSockRef, Deadline) ->
+
+ AccRef = make_ref(),
+ case prim_socket:accept(LSockRef, AccRef) of
+
+ {ok, SockRef} ->
+ Socket = #socket{ref = SockRef},
+ {ok, Socket};
+
+
+ select when (Deadline =:= nowait) ->
+ ?SELECT(accept, AccRef);
+
+ 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
+ {?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} = Socket, Data, Flags, Timeout)
+ when is_binary(Data), is_list(Flags) ->
+ To = undefined,
+ case deadline(Timeout) of
+ badarg = Reason ->
+ erlang:error(Reason, [Socket, Data, Flags, Timeout]);
+ Deadline ->
+ send_common(SockRef, Data, To, Flags, Deadline, send)
+ end;
+send(Socket, Data, Flags, Timeout) ->
+ erlang:error(badarg, [Socket, Data, Flags, Timeout]).
+
+
+send_common(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 -> 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, Flags, Deadline, SendName);
+
+ {?ESOCK_TAG, _Socket, select, SendRef} ->
+ send_common(
+ SockRef, Data, To, Flags, Deadline, SendName);
+
+ {?ESOCK_TAG, _Socket, abort, {SendRef, Reason}} ->
+ {error, {Reason, size(Data)}}
+
+ after Timeout ->
+ _ = cancel(SockRef, SendName, SendRef),
+ {error, {timeout, size(Data)}}
+ end;
+
+
+ select when (Deadline =:= nowait) ->
+ ?SELECT(SendName, SendRef);
+
+ select ->
+ Timeout = timeout(Deadline),
+ receive
+ {?ESOCK_TAG, _Socket, select, SendRef} ->
+ send_common(
+ SockRef, Data, To, Flags, 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, 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} = Socket, Data, Dest, Flags, Timeout)
+ when is_binary(Data), is_list(Flags) ->
+ case deadline(Timeout) of
+ badarg = Reason ->
+ erlang:error(Reason, [Socket, Data, Dest, Flags, Timeout]);
+ Deadline ->
+ send_common(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 :: 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} = Socket, MsgHdr, Flags, Timeout)
+ when is_map(MsgHdr), is_list(Flags) ->
+ case deadline(Timeout) of
+ badarg = Reason ->
+ erlang:error(Reason, [Socket, MsgHdr, Flags, Timeout]);
+ Deadline ->
+ do_sendmsg(SockRef, MsgHdr, Flags, Deadline)
+ end;
+sendmsg(Socket, MsgHdr, Flags, Timeout) ->
+ erlang:error(badarg, [Socket, MsgHdr, Flags, Timeout]).
+
+
+do_sendmsg(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, do_sendmsg_rest(maps:get(iov, MsgHdr), Written)};
+
+
+ select when (Deadline =:= nowait) ->
+ ?SELECT(sendmsg, SendRef);
+
+ select ->
+ Timeout = timeout(Deadline),
+ receive
+ {?ESOCK_TAG, #socket{ref = SockRef}, select, SendRef} ->
+ do_sendmsg(SockRef, MsgHdr, Flags, Deadline);
+
+ {?ESOCK_TAG, _Socket, abort, {SendRef, Reason}} ->
+ {error, Reason}
+
+ after Timeout ->
+ _ = cancel(SockRef, sendmsg, SendRef),
+ {error, timeout}
+ 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, _} = 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].
+
+
+%% ===========================================================================
+%%
+%% 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} = Socket, Length, Flags, Timeout)
+ when is_integer(Length), Length >= 0, is_list(Flags) ->
+ case deadline(Timeout) of
+ badarg = Reason ->
+ erlang:error(Reason, [Socket, Length, Flags, Timeout]);
+ Deadline ->
+ do_recv(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
+%%
+%% 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, Flags, Deadline, Acc) ->
+
+ RecvRef = make_ref(),
+
+ case prim_socket:recv(SockRef, RecvRef, Length, Flags) of
+
+ {ok, Bin} ->
+ {ok, bincat(Acc, Bin)};
+
+
+ {more, Bin} ->
+ %% There is more data readily available
+ %% - repeat unless time's up
+ Timeout = timeout(Deadline),
+ if
+ 0 < Timeout ->
+ do_recv(
+ SockRef, Length, Flags, Deadline, bincat(Acc, Bin));
+ true ->
+ {ok, bincat(Acc, Bin)}
+ end;
+
+ {select, Bin} when Deadline =:= nowait ->
+ %% We got a chunk but the caller will not wait
+ {ok, {bincat(Acc, Bin), ?SELECT_INFO(recv, RecvRef)}};
+
+ {select, Bin} ->
+ %% We got a chunk
+ Timeout = timeout(Deadline),
+ receive
+ {?ESOCK_TAG, #socket{ref = SockRef}, select, RecvRef} ->
+ if
+ 0 < Timeout ->
+ do_recv(
+ SockRef, Length - byte_size(Bin), Flags,
+ 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;
+
+
+ select when Deadline =:= nowait ->
+ %% The caller does not want to wait!
+ %% The caller will be informed that there is something to read
+ %% via the select socket message (see below).
+ if
+ byte_size(Acc) =:= 0 ->
+ ?SELECT(recv, RecvRef);
+ true ->
+ {ok, {Acc, ?SELECT_INFO(recv, RecvRef)}}
+ end;
+
+ select when Length =:= 0, 0 < byte_size(Acc) ->
+ %% We got some 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
+ {?ESOCK_TAG, #socket{ref = SockRef}, select, RecvRef} ->
+ if
+ 0 < Timeout ->
+ do_recv(
+ SockRef, Length, Flags, 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, 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);
+
+ {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} = Socket, BufSz, Flags, Timeout)
+ when is_integer(BufSz), 0 =< BufSz, is_list(Flags) ->
+ case deadline(Timeout) of
+ badarg = Reason ->
+ erlang:error(Reason, [Socket, BufSz, Flags, Timeout]);
+ Deadline ->
+ do_recvfrom(SockRef, BufSz, Flags, Deadline)
+ end;
+recvfrom(Socket, BufSz, Flags, Timeout) ->
+ erlang:error(badarg, [Socket, BufSz, Flags, Timeout]).
+
+
+do_recvfrom(SockRef, BufSz, Flags, Deadline) ->
+
+ RecvRef = make_ref(),
+
+ case prim_socket:recvfrom(SockRef, RecvRef, BufSz, Flags) of
+
+ {ok, {_Source, _NewData}} = OK ->
+ OK;
+
+
+ select when Deadline =:= nowait ->
+ ?SELECT(recvfrom, RecvRef);
+
+ select ->
+ %% 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, Flags, Deadline);
+
+ {?ESOCK_TAG, _Socket, abort, {RecvRef, Reason}} ->
+ {error, Reason}
+
+ after Timeout ->
+ cancel(SockRef, recvfrom, RecvRef),
+ {error, timeout}
+ end;
+
+
+ {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, _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} = Socket, BufSz, CtrlSz, Flags, Timeout)
+ when 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]);
+ Deadline ->
+ do_recvmsg(SockRef, BufSz, CtrlSz, Flags, Deadline)
+ end;
+recvmsg(Socket, BufSz, CtrlSz, Flags, Timeout) ->
+ erlang:error(badarg, [Socket, BufSz, CtrlSz, Flags, Timeout]).
+
+
+do_recvmsg(SockRef, BufSz, CtrlSz, Flags, Deadline) ->
+
+ RecvRef = make_ref(),
+ case prim_socket:recvmsg(SockRef, RecvRef, BufSz, CtrlSz, Flags) of
+
+ {ok, _MsgHdr} = OK ->
+ OK;
+
+
+ select when Deadline =:= nowait ->
+ ?SELECT(recvmsg, RecvRef);
+
+ select ->
+ %% 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, Flags, Deadline);
+
+ {?ESOCK_TAG, _Socket, abort, {RecvRef, Reason}} ->
+ {error, Reason}
+
+ after Timeout ->
+ cancel(SockRef, recvmsg, RecvRef),
+ {error, timeout}
+ end;
+
+
+ {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, _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 :: term().
+
+close(#socket{ref = 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
+ {?ESOCK_TAG, #socket{ref = 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 :: term().
+
+shutdown(#socket{ref = SockRef}, How) ->
+ 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 :: 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) ->
+ 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 :: 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) ->
+ 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 :: term().
+
+sockname(#socket{ref = 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 :: term().
+
+peername(#socket{ref = 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 :: term().
+
+cancel(#socket{ref = SockRef}, ?SELECT_INFO(Tag, Ref)) ->
+ 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;
+ 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.
+
+
+%% ===========================================================================
+%%
+%% 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;
+ _ when is_integer(Timeout), 0 =< Timeout ->
+ timestamp() + Timeout;
+ _ -> badarg
+ 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]).
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