diff options
author | Raimo Niskanen <raimo@erlang.org> | 2020-04-01 15:16:54 +0200 |
---|---|---|
committer | Raimo Niskanen <raimo@erlang.org> | 2020-04-01 15:54:14 +0200 |
commit | 54ae202df1511fe611d682f128970505cd0393f4 (patch) | |
tree | 9eae6c670788f0cc1cf74fbb8574b115252289b7 | |
parent | 54aad59898321ae57b282519268ebff895beb37d (diff) | |
download | erlang-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/Makefile | 30 | ||||
-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.in | 7 | ||||
-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.h | 2 | ||||
-rw-r--r-- | erts/emulator/test/Makefile | 24 | ||||
-rw-r--r-- | erts/preloaded/ebin/erl_init.beam | bin | 2296 -> 2316 bytes | |||
-rw-r--r-- | erts/preloaded/ebin/prim_net.beam | bin | 5140 -> 4900 bytes | |||
-rw-r--r-- | erts/preloaded/ebin/prim_socket.beam | bin | 0 -> 31976 bytes | |||
-rw-r--r-- | erts/preloaded/ebin/socket.beam | bin | 81284 -> 0 bytes | |||
-rw-r--r-- | erts/preloaded/src/Makefile | 4 | ||||
-rw-r--r-- | erts/preloaded/src/erl_init.erl | 4 | ||||
-rw-r--r-- | erts/preloaded/src/prim_net.erl | 17 | ||||
-rw-r--r-- | erts/preloaded/src/prim_socket.erl | 1390 | ||||
-rw-r--r-- | erts/preloaded/src/socket.erl | 4192 | ||||
-rw-r--r-- | lib/kernel/doc/src/Makefile | 5 | ||||
-rw-r--r-- | lib/kernel/doc/src/part.xml | 1 | ||||
-rw-r--r-- | lib/kernel/doc/src/ref_man.xml | 1 | ||||
-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.xml | 1 | ||||
-rw-r--r-- | lib/kernel/src/Makefile | 1 | ||||
-rw-r--r-- | lib/kernel/src/net.erl | 22 | ||||
-rw-r--r-- | lib/kernel/src/socket.erl | 2445 | ||||
-rw-r--r-- | lib/kernel/test/Makefile | 22 | ||||
-rw-r--r-- | lib/kernel/test/net_SUITE.erl | 19 | ||||
-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 Binary files differindex bcfc4d2e22..4f22f77a27 100644 --- a/erts/preloaded/ebin/erl_init.beam +++ b/erts/preloaded/ebin/erl_init.beam diff --git a/erts/preloaded/ebin/prim_net.beam b/erts/preloaded/ebin/prim_net.beam Binary files differindex 6e8073ae5a..510856e5c0 100644 --- a/erts/preloaded/ebin/prim_net.beam +++ b/erts/preloaded/ebin/prim_net.beam diff --git a/erts/preloaded/ebin/prim_socket.beam b/erts/preloaded/ebin/prim_socket.beam Binary files differnew file mode 100644 index 0000000000..df22e206de --- /dev/null +++ b/erts/preloaded/ebin/prim_socket.beam diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam Binary files differdeleted file mode 100644 index e303eaa71d..0000000000 --- a/erts/preloaded/ebin/socket.beam +++ /dev/null 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 |