aboutsummaryrefslogtreecommitdiffstats
path: root/erts
diff options
context:
space:
mode:
authorMicael Karlberg <[email protected]>2018-08-01 19:42:32 +0200
committerMicael Karlberg <[email protected]>2018-09-18 14:50:18 +0200
commit90a150771faa3cf01e82919b0c17854de9987783 (patch)
tree5ad6c69f9ec4e569bba3da11df10d379faaba537 /erts
parent25c38eff5c1e8d4dc6325afa62031874e23262dc (diff)
downloadotp-90a150771faa3cf01e82919b0c17854de9987783.tar.gz
otp-90a150771faa3cf01e82919b0c17854de9987783.tar.bz2
otp-90a150771faa3cf01e82919b0c17854de9987783.zip
[socket-nif] Processing of more cmsg headers
Added processing or more cmsg headers (for more options). Now (also) supports: socket:timestamp. Also various fixes and cleanups. For some reason calling getopt(Sock, 0, {13, int}) (or similar) fails with badarg even though the nif-function (nif_getopt) actually returns a valid value (for instance: {ok, 0}). OTP-14831
Diffstat (limited to 'erts')
-rw-r--r--erts/doc/src/socket.xml26
-rw-r--r--erts/doc/src/socket_usage.xml2
-rw-r--r--erts/emulator/nifs/common/socket_int.h5
-rw-r--r--erts/emulator/nifs/common/socket_nif.c345
-rw-r--r--erts/emulator/nifs/common/socket_util.c73
-rw-r--r--erts/emulator/nifs/common/socket_util.h6
-rw-r--r--erts/preloaded/ebin/socket.beambin61376 -> 61704 bytes
-rw-r--r--erts/preloaded/src/socket.erl52
8 files changed, 375 insertions, 134 deletions
diff --git a/erts/doc/src/socket.xml b/erts/doc/src/socket.xml
index 53d1516f1e..2fb922408b 100644
--- a/erts/doc/src/socket.xml
+++ b/erts/doc/src/socket.xml
@@ -120,7 +120,7 @@
<name name="timeval"/>
</datatype>
<datatype>
- <name name="ip_tos_flag"/>
+ <name name="ip_tos"/>
</datatype>
<datatype>
<name name="ip_mreq"/>
@@ -138,6 +138,9 @@
<name name="ip_msfilter"/>
</datatype>
<datatype>
+ <name name="ip_pktinfo"/>
+ </datatype>
+ <datatype>
<name name="ipv6_mreq"/>
</datatype>
<datatype>
@@ -156,6 +159,27 @@
<name name="sctp_rtoinfo"/>
</datatype>
<datatype>
+ <name name="msghdr_flag"/>
+ </datatype>
+ <datatype>
+ <name name="msghdr_flags"/>
+ </datatype>
+ <datatype>
+ <name name="msghdr"/>
+ </datatype>
+ <datatype>
+ <name name="cmsghdr_level"/>
+ </datatype>
+ <datatype>
+ <name name="cmsghdr_type"/>
+ </datatype>
+ <datatype>
+ <name name="cmsghdr_data"/>
+ </datatype>
+ <datatype>
+ <name name="cmsghdr"/>
+ </datatype>
+ <datatype>
<name name="uint16"/>
</datatype>
<datatype>
diff --git a/erts/doc/src/socket_usage.xml b/erts/doc/src/socket_usage.xml
index 933341bd35..b7459e97fa 100644
--- a/erts/doc/src/socket_usage.xml
+++ b/erts/doc/src/socket_usage.xml
@@ -444,7 +444,7 @@
<cell>ip_tos()</cell>
<cell>yes</cell>
<cell>yes</cell>
- <cell>may require admin capability</cell>
+ <cell>some high-priority levels may require superuser capability</cell>
</row>
<row>
<cell>transparent</cell>
diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h
index 80a9eb0734..3595c483d7 100644
--- a/erts/emulator/nifs/common/socket_int.h
+++ b/erts/emulator/nifs/common/socket_int.h
@@ -142,11 +142,14 @@ extern ERL_NIF_TERM esock_atom_reliability;
extern ERL_NIF_TERM esock_atom_rights;
extern ERL_NIF_TERM esock_atom_scope_id;
extern ERL_NIF_TERM esock_atom_sctp;
+extern ERL_NIF_TERM esock_atom_sec;
extern ERL_NIF_TERM esock_atom_seqpacket;
+extern ERL_NIF_TERM esock_atom_socket;
extern ERL_NIF_TERM esock_atom_spec_dst;
extern ERL_NIF_TERM esock_atom_stream;
extern ERL_NIF_TERM esock_atom_tcp;
extern ERL_NIF_TERM esock_atom_throughput;
+extern ERL_NIF_TERM esock_atom_timestamp;
extern ERL_NIF_TERM esock_atom_tos;
extern ERL_NIF_TERM esock_atom_true;
extern ERL_NIF_TERM esock_atom_trunc;
@@ -154,6 +157,8 @@ extern ERL_NIF_TERM esock_atom_ttl;
extern ERL_NIF_TERM esock_atom_type;
extern ERL_NIF_TERM esock_atom_udp;
extern ERL_NIF_TERM esock_atom_undefined;
+extern ERL_NIF_TERM esock_atom_unknown;
+extern ERL_NIF_TERM esock_atom_usec;
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c
index 4b4082062b..4144341d71 100644
--- a/erts/emulator/nifs/common/socket_nif.c
+++ b/erts/emulator/nifs/common/socket_nif.c
@@ -421,6 +421,11 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL;
#define SOCKET_RECV_BUFFER_SIZE_DEFAULT 2048
#define SOCKET_RECV_CTRL_BUFFER_SIZE_DEFAULT 1024
+#define VT2S(__VT__) (((__VT__) == SOCKET_OPT_VALUE_TYPE_UNSPEC) ? "unspec" : \
+ (((__VT__) == SOCKET_OPT_VALUE_TYPE_INT) ? "int" : \
+ ((__VT__) == SOCKET_OPT_VALUE_TYPE_BOOL) ? "bool" : \
+ "undef"))
+
#define SOCKET_OPT_VALUE_TYPE_UNSPEC 0
#define SOCKET_OPT_VALUE_TYPE_INT 1
#define SOCKET_OPT_VALUE_TYPE_BOOL 2
@@ -1468,7 +1473,7 @@ static ERL_NIF_TERM ngetopt(ErlNifEnv* env,
BOOLEAN_T isEncoded,
BOOLEAN_T isOTP,
int level,
- int eOpt);
+ ERL_NIF_TERM eOpt);
static ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env,
SocketDescriptor* descP,
int eOpt);
@@ -1479,7 +1484,7 @@ static ERL_NIF_TERM ngetopt_otp_iow(ErlNifEnv* env,
static ERL_NIF_TERM ngetopt_native(ErlNifEnv* env,
SocketDescriptor* descP,
int level,
- int eOpt);
+ ERL_NIF_TERM eOpt);
static ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env,
SocketDescriptor* descP,
int level,
@@ -1897,6 +1902,9 @@ extern char* encode_cmsghdrs(ErlNifEnv* env,
ErlNifBinary* cmsgBinP,
struct msghdr* msgHdrP,
ERL_NIF_TERM* eCMsgHdr);
+static char* encode_cmsghdr_level(ErlNifEnv* env,
+ int level,
+ ERL_NIF_TERM* eLevel);
static char* encode_cmsghdr_type(ErlNifEnv* env,
int level,
int type,
@@ -1909,6 +1917,13 @@ static char* encode_cmsghdr_data(ErlNifEnv* env,
size_t dataPos,
size_t dataLen,
ERL_NIF_TERM* eCMsgHdrData);
+static char* encode_cmsghdr_data_socket(ErlNifEnv* env,
+ ERL_NIF_TERM ctrlBuf,
+ int type,
+ unsigned char* dataP,
+ size_t dataPos,
+ size_t dataLen,
+ ERL_NIF_TERM* eCMsgHdrData);
static char* encode_cmsghdr_data_ip(ErlNifEnv* env,
ERL_NIF_TERM ctrlBuf,
int type,
@@ -2104,6 +2119,7 @@ static char str_association[] = "association";
static char str_assoc_id[] = "assoc_id";
static char str_authentication[] = "authentication";
// static char str_any[] = "any";
+static char str_bool[] = "bool";
static char str_close[] = "close";
static char str_closed[] = "closed";
static char str_closing[] = "closing";
@@ -2118,6 +2134,7 @@ static char str_in4_sockaddr[] = "in4_sockaddr";
static char str_in6_sockaddr[] = "in6_sockaddr";
static char str_include[] = "include";
static char str_initial[] = "initial";
+static char str_int[] = "int";
static char str_interface[] = "interface";
static char str_iow[] = "iow";
static char str_local_rwnd[] = "local_rwnd";
@@ -2149,7 +2166,6 @@ static char str_partial_delivery[] = "partial_delivery";
static char str_peer_error[] = "peer_error";
static char str_peer_rwnd[] = "peer_rwnd";
static char str_probe[] = "probe";
-static char str_sec[] = "sec";
static char str_select[] = "select";
static char str_sender_dry[] = "sender_dry";
static char str_send_failure[] = "send_failure";
@@ -2158,7 +2174,6 @@ static char str_slist[] = "slist";
static char str_sourceaddr[] = "sourceaddr";
static char str_timeout[] = "timeout";
static char str_true[] = "true";
-static char str_usec[] = "usec";
static char str_want[] = "want";
/* (special) error string constants */
@@ -2213,11 +2228,14 @@ ERL_NIF_TERM esock_atom_rights;
ERL_NIF_TERM esock_atom_reliability;
ERL_NIF_TERM esock_atom_scope_id;
ERL_NIF_TERM esock_atom_sctp;
+ERL_NIF_TERM esock_atom_sec;
ERL_NIF_TERM esock_atom_seqpacket;
+ERL_NIF_TERM esock_atom_socket;
ERL_NIF_TERM esock_atom_spec_dst;
ERL_NIF_TERM esock_atom_stream;
ERL_NIF_TERM esock_atom_tcp;
ERL_NIF_TERM esock_atom_throughput;
+ERL_NIF_TERM esock_atom_timestamp;
ERL_NIF_TERM esock_atom_tos;
ERL_NIF_TERM esock_atom_true;
ERL_NIF_TERM esock_atom_trunc;
@@ -2226,6 +2244,7 @@ ERL_NIF_TERM esock_atom_type;
ERL_NIF_TERM esock_atom_udp;
ERL_NIF_TERM esock_atom_undefined;
ERL_NIF_TERM esock_atom_unknown;
+ERL_NIF_TERM esock_atom_usec;
/* *** "Global" error (=reason) atoms *** */
ERL_NIF_TERM esock_atom_eagain;
@@ -2238,6 +2257,7 @@ static ERL_NIF_TERM atom_address;
static ERL_NIF_TERM atom_association;
static ERL_NIF_TERM atom_assoc_id;
static ERL_NIF_TERM atom_authentication;
+static ERL_NIF_TERM atom_bool;
static ERL_NIF_TERM atom_close;
static ERL_NIF_TERM atom_closed;
static ERL_NIF_TERM atom_closing;
@@ -2252,6 +2272,7 @@ static ERL_NIF_TERM atom_in4_sockaddr;
static ERL_NIF_TERM atom_in6_sockaddr;
static ERL_NIF_TERM atom_include;
static ERL_NIF_TERM atom_initial;
+static ERL_NIF_TERM atom_int;
static ERL_NIF_TERM atom_interface;
static ERL_NIF_TERM atom_iow;
static ERL_NIF_TERM atom_local_rwnd;
@@ -2282,7 +2303,6 @@ static ERL_NIF_TERM atom_partial_delivery;
static ERL_NIF_TERM atom_peer_error;
static ERL_NIF_TERM atom_peer_rwnd;
static ERL_NIF_TERM atom_probe;
-static ERL_NIF_TERM atom_sec;
static ERL_NIF_TERM atom_select;
static ERL_NIF_TERM atom_sender_dry;
static ERL_NIF_TERM atom_send_failure;
@@ -2291,7 +2311,6 @@ static ERL_NIF_TERM atom_slist;
static ERL_NIF_TERM atom_sourceaddr;
static ERL_NIF_TERM atom_timeout;
static ERL_NIF_TERM atom_true;
-static ERL_NIF_TERM atom_usec;
static ERL_NIF_TERM atom_want;
static ERL_NIF_TERM atom_eisconn;
@@ -7424,43 +7443,19 @@ ERL_NIF_TERM nsetopt_timeval_opt(ErlNifEnv* env,
ERL_NIF_TERM eVal)
{
ERL_NIF_TERM result;
- ERL_NIF_TERM eSec, eUSec;
struct timeval timeVal;
int res;
- size_t sz;
+ char* xres;
SSDBG( descP,
("SOCKET", "nsetopt_timeval_opt -> entry with"
"\r\n eVal: %T"
"\r\n", eVal) );
- // It must be a map
- if (!IS_MAP(env, eVal))
- return esock_make_error(env, esock_atom_einval);
-
- // It must have atleast ten attributes
- if (!enif_get_map_size(env, eVal, &sz) || (sz < 2))
- return esock_make_error(env, esock_atom_einval);
-
- SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_sctp_rtoinfo -> extract attributes\r\n") );
-
- if (!GET_MAP_VAL(env, eVal, atom_sec, &eSec))
- return esock_make_error(env, esock_atom_einval);
-
- if (!GET_MAP_VAL(env, eVal, atom_usec, &eUSec))
- return esock_make_error(env, esock_atom_einval);
+ if ((xres = esock_decode_timeval(env, eVal, &timeVal)) != NULL)
+ return esock_make_error_str(env, xres);
SSDBG( descP,
- ("SOCKET", "nsetopt_timeval_opt -> decode attributes\r\n") );
-
- if (!GET_LONG(env, eSec, &timeVal.tv_sec))
- return esock_make_error(env, esock_atom_einval);
-
- if (!GET_LONG(env, eUSec, &timeVal.tv_usec))
- return esock_make_error(env, esock_atom_einval);
-
- SSDBG( descP,
("SOCKET", "nsetopt_timeval_opt -> set timeval option\r\n") );
res = socket_setopt(descP->sock, level, opt, &timeVal, sizeof(timeVal));
@@ -7668,26 +7663,26 @@ ERL_NIF_TERM nif_getopt(ErlNifEnv* env,
{
SocketDescriptor* descP;
int eLevel, level = -1;
- int eOpt;
- ERL_NIF_TERM eIsEncoded;
+ ERL_NIF_TERM eIsEncoded, eOpt;
BOOLEAN_T isEncoded, isOTP;
SGDBG( ("SOCKET", "nif_getopt -> entry with argc: %d\r\n", argc) );
if ((argc != 4) ||
!enif_get_resource(env, argv[0], sockets, (void**) &descP) ||
- !GET_INT(env, argv[2], &eLevel) ||
- !GET_INT(env, argv[3], &eOpt)) {
+ !GET_INT(env, argv[2], &eLevel)) {
+ SGDBG( ("SOCKET", "nif_getopt -> failed processing args\r\n") );
return enif_make_badarg(env);
}
eIsEncoded = argv[1];
+ eOpt = argv[3]; // Is "normally" an int, but if raw mode: {Int, ValueSz}
SSDBG( descP,
("SOCKET", "nif_getopt -> args when sock = %d:"
"\r\n Socket: %T"
"\r\n eIsEncoded: %T"
"\r\n eLevel: %d"
- "\r\n eOpt: %d"
+ "\r\n eOpt: %T"
"\r\n", descP->sock, argv[0], eIsEncoded, eLevel, eOpt) );
isEncoded = esock_decode_bool(eIsEncoded);
@@ -7706,27 +7701,34 @@ ERL_NIF_TERM ngetopt(ErlNifEnv* env,
BOOLEAN_T isEncoded,
BOOLEAN_T isOTP,
int level,
- int eOpt)
+ ERL_NIF_TERM eOpt)
{
ERL_NIF_TERM result;
+ int opt;
SSDBG( descP,
("SOCKET", "ngetopt -> entry with"
- "\r\n isEncoded: %d"
- "\r\n isOTP: %d"
+ "\r\n isEncoded: %s"
+ "\r\n isOTP: %s"
"\r\n level: %d"
- "\r\n eOpt: %d"
- "\r\n", isEncoded, isOTP, level, eOpt) );
+ "\r\n eOpt: %T"
+ "\r\n", B2S(isEncoded), B2S(isOTP), level, eOpt) );
if (isOTP) {
/* These are not actual socket options,
* but options for our implementation.
*/
- result = ngetopt_otp(env, descP, eOpt);
+ if (GET_INT(env, eOpt, &opt))
+ result = ngetopt_otp(env, descP, opt);
+ else
+ result = esock_make_error(env, esock_atom_einval);
} else if (!isEncoded) {
result = ngetopt_native(env, descP, level, eOpt);
} else {
- result = ngetopt_level(env, descP, level, eOpt);
+ if (GET_INT(env, eOpt, &opt))
+ result = ngetopt_level(env, descP, level, opt);
+ else
+ result = esock_make_error(env, esock_atom_einval);
}
SSDBG( descP,
@@ -7809,7 +7811,7 @@ static
ERL_NIF_TERM ngetopt_native(ErlNifEnv* env,
SocketDescriptor* descP,
int level,
- int eOpt)
+ ERL_NIF_TERM eOpt)
{
ERL_NIF_TERM result = enif_make_badarg(env);
int opt;
@@ -7819,16 +7821,23 @@ ERL_NIF_TERM ngetopt_native(ErlNifEnv* env,
SSDBG( descP,
("SOCKET", "ngetopt_native -> entry with"
"\r\n level: %d"
- "\r\n eOpt: %d"
+ "\r\n eOpt: %T"
"\r\n", level, eOpt) );
/* <KOLLA>
- * We should really make it possible to specify common specific types,
+ * We should really make it possible to specify more common specific types,
* such as integer or boolean (instead of the size)...
* </KOLLA>
*/
if (decode_native_get_opt(env, eOpt, &opt, &valueType, (int*) &valueSz)) {
+
+ SSDBG( descP,
+ ("SOCKET", "ngetopt_native -> decoded opt"
+ "\r\n valueType: %d (%s)"
+ "\r\n ValueSize: %d"
+ "\r\n", valueType, VT2S(valueType), valueSz) );
+
switch (valueType) {
case SOCKET_OPT_VALUE_TYPE_UNSPEC:
result = ngetopt_native_unspec(env, descP, level, opt, valueSz);
@@ -7863,7 +7872,7 @@ ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env,
int opt,
SOCKOPTLEN_T valueSz)
{
- ERL_NIF_TERM result = enif_make_badarg(env);
+ ERL_NIF_TERM result = esock_make_error(env, esock_atom_einval);
int res;
SSDBG( descP,
@@ -7880,19 +7889,33 @@ ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env,
else
result = esock_atom_ok;
} else {
+ SOCKOPTLEN_T vsz = valueSz;
ErlNifBinary val;
- if (ALLOC_BIN(valueSz, &val)) {
- res = sock_getopt(descP->sock, level, opt, val.data, &valueSz);
+ SSDBG( descP, ("SOCKET", "ngetopt_native_unspec -> try alloc buffer\r\n") );
+
+ if (ALLOC_BIN(vsz, &val)) {
+ int saveErrno;
+ res = sock_getopt(descP->sock, level, opt, val.data, &vsz);
if (res != 0) {
- result = esock_make_error_errno(env, sock_errno());
+ saveErrno = sock_errno();
+
+ result = esock_make_error_errno(env, saveErrno);
} else {
- if (valueSz < val.size) {
- if (REALLOC_BIN(&val, valueSz)) {
- result = esock_make_ok2(env, MKBIN(env, &val));
- } else {
- result = enif_make_badarg(env);
- }
+
+ /* Did we use all of the buffer? */
+ if (vsz == val.size) {
+
+ result = esock_make_ok2(env, MKBIN(env, &val));
+
+ } else {
+
+ ERL_NIF_TERM tmp;
+
+ tmp = MKBIN(env, &val);
+ tmp = MKSBIN(env, tmp, 0, vsz);
+
+ result = esock_make_ok2(env, tmp);
}
}
} else {
@@ -9959,18 +9982,12 @@ ERL_NIF_TERM ngetopt_timeval_opt(ErlNifEnv* env,
result = esock_make_error_errno(env, sock_errno());
} else {
ERL_NIF_TERM eTimeVal;
- ERL_NIF_TERM keys[] = {atom_sec, atom_usec};
- ERL_NIF_TERM vals[] = {MKL(env, val.tv_sec), MKL(env, val.tv_usec)};
-
- unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM);
- unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM);
-
- ESOCK_ASSERT( (numKeys == numVals) );
-
- if (!MKMA(env, keys, vals, numKeys, &eTimeVal))
- return esock_make_error(env, esock_atom_einval);
-
- result = esock_make_ok2(env, eTimeVal);
+ char* xres;
+
+ if ((xres = esock_encode_timeval(env, &val, &eTimeVal)) != NULL)
+ result = esock_make_error_str(env, xres);
+ else
+ result = esock_make_ok2(env, eTimeVal);
}
SSDBG( descP,
@@ -10562,6 +10579,28 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env,
"\r\n", read, saveErrno, recvRef) );
+ /* <KOLLA>
+ *
+ * We need to handle read = 0 for other type(s) (DGRAM) when
+ * its actually valid to read 0 bytes.
+ *
+ * </KOLLA>
+ */
+
+ if ((read == 0) && (descP->type == SOCK_STREAM)) {
+
+ /*
+ * When a stream socket peer has performed an orderly shutdown, the return
+ * value will be 0 (the traditional "end-of-file" return).
+ *
+ * *We* do never actually try to read 0 bytes from a stream socket!
+ */
+
+ return esock_make_error(env, atom_closed);
+
+ }
+
+
/* There is a special case: If the provided 'to read' value is
* zero (0). That means that we reads as much as we can, using
* the default read buffer size.
@@ -10575,7 +10614,7 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env,
/* +++ Oups - closed +++ */
- SSDBG( descP, ("SOCKET", "recvfrom_check_result -> closed\r\n") );
+ SSDBG( descP, ("SOCKET", "recvmsg_check_result -> closed\r\n") );
/* <KOLLA>
* IF THE CURRENT PROCESS IS *NOT* THE CONTROLLING
@@ -10599,7 +10638,7 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env,
} else if ((saveErrno == ERRNO_BLOCK) ||
(saveErrno == EAGAIN)) {
- SSDBG( descP, ("SOCKET", "recvfrom_check_result -> eagain\r\n") );
+ SSDBG( descP, ("SOCKET", "recvmsg_check_result -> eagain\r\n") );
SELECT(env, descP->sock, (ERL_NIF_SELECT_READ),
descP, NULL, recvRef);
@@ -10609,7 +10648,7 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env,
SSDBG( descP,
("SOCKET",
- "recvfrom_check_result -> errno: %d\r\n", saveErrno) );
+ "recvmsg_check_result -> errno: %d\r\n", saveErrno) );
return esock_make_error_errno(env, saveErrno);
}
@@ -10637,7 +10676,7 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env,
SSDBG( descP,
("SOCKET",
- "recvfrom_check_result -> "
+ "recvmsg_check_result -> "
"(msghdr) encode failed: %s\r\n", xres) );
return esock_make_error_str(env, xres);
@@ -10645,7 +10684,7 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env,
SSDBG( descP,
("SOCKET",
- "recvfrom_check_result -> "
+ "recvmsg_check_result -> "
"(msghdr) encode ok: %T\r\n", eMsgHdr) );
return esock_make_ok2(env, eMsgHdr);
@@ -10684,12 +10723,19 @@ char* encode_msghdr(ErlNifEnv* env,
("SOCKET", "encode_msghdr -> entry with"
"\r\n read: %d"
"\r\n", read) );
-
- if ((xres = esock_encode_sockaddr(env,
- (SocketAddress*) msgHdrP->msg_name,
- msgHdrP->msg_namelen,
- &addr)) != NULL)
- return xres;
+
+ /* The address is not used if we are connected,
+ * so check (length = 0) before we try to encodel
+ */
+ if (msgHdrP->msg_namelen != 0) {
+ if ((xres = esock_encode_sockaddr(env,
+ (SocketAddress*) msgHdrP->msg_name,
+ msgHdrP->msg_namelen,
+ &addr)) != NULL)
+ return xres;
+ } else {
+ addr = esock_atom_undefined;
+ }
SSDBG( descP, ("SOCKET", "encode_msghdr -> try encode iov\r\n") );
if ((xres = esock_encode_iov(env,
@@ -10814,7 +10860,7 @@ char* encode_cmsghdrs(ErlNifEnv* env,
* so if its a protocol we don't know, we return its integer
* value and leave it to the user.
*/
- if (esock_encode_protocol(env, currentP->cmsg_level, &level) != NULL)
+ if (encode_cmsghdr_level(env, currentP->cmsg_level, &level) != NULL)
level = MKI(env, currentP->cmsg_level);
if (encode_cmsghdr_type(env,
@@ -10875,6 +10921,35 @@ char* encode_cmsghdrs(ErlNifEnv* env,
+/* +++ encode_cmsghdr_level +++
+ *
+ * Encode the type part of the cmsghdr().
+ *
+ */
+
+static
+char* encode_cmsghdr_level(ErlNifEnv* env,
+ int level,
+ ERL_NIF_TERM* eLevel)
+{
+ char* xres;
+
+ switch (level) {
+ case SOL_SOCKET:
+ *eLevel = esock_atom_socket;
+ xres = NULL;
+ break;
+
+ default:
+ xres = esock_encode_protocol(env, level, eLevel);
+ break;
+ }
+
+ return xres;
+}
+
+
+
/* +++ encode_cmsghdr_type +++
*
* Encode the type part of the cmsghdr().
@@ -10892,6 +10967,12 @@ char* encode_cmsghdr_type(ErlNifEnv* env,
switch (level) {
case SOL_SOCKET:
switch (type) {
+#if defined(SO_TIMESTAMP)
+ case SO_TIMESTAMP:
+ *eType = esock_atom_timestamp;
+ break;
+#endif
+
#if defined(SCM_RIGHTS)
case SCM_RIGHTS:
*eType = esock_atom_rights;
@@ -11011,13 +11092,13 @@ char* encode_cmsghdr_data(ErlNifEnv* env,
char* xres;
switch (level) {
- /*
- #if defined(SOL_SOCKET)
- case SOL_SOCKET:
- xres = encode_cmsghdr_data_socket(env, type, dataP, eCMsgHdrData);
- break;
- #endif
- */
+#if defined(SOL_SOCKET)
+ case SOL_SOCKET:
+ xres = encode_cmsghdr_data_socket(env, ctrlBuf, type,
+ dataP, dataPos, dataLen,
+ eCMsgHdrData);
+ break;
+#endif
#if defined(SOL_IP)
case SOL_IP:
@@ -11068,6 +11149,45 @@ char* encode_cmsghdr_data(ErlNifEnv* env,
+/* +++ encode_cmsghdr_data_socket +++
+ *
+ * Encode the data part when "protocol" = socket of the cmsghdr().
+ *
+ */
+
+static
+char* encode_cmsghdr_data_socket(ErlNifEnv* env,
+ ERL_NIF_TERM ctrlBuf,
+ int type,
+ unsigned char* dataP,
+ size_t dataPos,
+ size_t dataLen,
+ ERL_NIF_TERM* eCMsgHdrData)
+{
+ // char* xres;
+
+ switch (type) {
+#if defined(SO_TIMESTAMP)
+ case SO_TIMESTAMP:
+ {
+ struct timeval* timeP = (struct timeval*) dataP;
+
+ if (esock_encode_timeval(env, timeP, eCMsgHdrData) != NULL)
+ *eCMsgHdrData = MKSBIN(env, ctrlBuf, dataPos, dataLen);
+ }
+ break;
+#endif
+
+ default:
+ *eCMsgHdrData = MKSBIN(env, ctrlBuf, dataPos, dataLen);
+ break;
+ }
+
+ return NULL;
+}
+
+
+
/* +++ encode_cmsghdr_data_ip +++
*
* Encode the data part when protocol = IP of the cmsghdr().
@@ -11089,8 +11209,8 @@ char* encode_cmsghdr_data_ip(ErlNifEnv* env,
#if defined(IP_TOS)
case IP_TOS:
{
- unsigned char tos = IPTOS_TOS(*dataP);
- switch (tos) {
+ unsigned char tos = *dataP;
+ switch (IPTOS_TOS(tos)) {
case IPTOS_LOWDELAY:
*eCMsgHdrData = esock_atom_lowdelay;
break;
@@ -11551,29 +11671,21 @@ BOOLEAN_T decode_native_get_opt(ErlNifEnv* env, ERL_NIF_TERM eVal,
return FALSE;
if (IS_ATOM(env, nativeOptT[1])) {
- unsigned int len;
- char t[16]; // Just in case
-
- if (!(GET_ATOM_LEN(env, nativeOptT[1], &len) &&
- (len > 0) &&
- (len <= (sizeof("bool")))))
- return FALSE;
- if (!GET_ATOM(env, nativeOptT[1], t, sizeof(t)))
- return FALSE;
-
- if (strncmp(t, "bool", len) == 0) {
- *valueType = SOCKET_OPT_VALUE_TYPE_BOOL;
- *valueSz = sizeof(int); // Just to be sure
- } else if (strncmp(t, "int", len) == 0) {
+ if (COMPARE(nativeOptT[1], atom_int) == 0) {
+ SGDBG( ("SOCKET", "decode_native_get_opt -> int\r\n") );
*valueType = SOCKET_OPT_VALUE_TYPE_INT;
*valueSz = sizeof(int); // Just to be sure
+ } else if (COMPARE(nativeOptT[1], atom_bool) == 0) {
+ SGDBG( ("SOCKET", "decode_native_get_opt -> bool\r\n") );
+ *valueType = SOCKET_OPT_VALUE_TYPE_BOOL;
+ *valueSz = sizeof(int); // Just to be sure
} else {
return FALSE;
}
-
} else if (IS_NUM(env, nativeOptT[1])) {
if (GET_INT(env, nativeOptT[1], valueSz)) {
+ SGDBG( ("SOCKET", "decode_native_get_opt -> unspec\r\n") );
*valueType = SOCKET_OPT_VALUE_TYPE_UNSPEC;
} else {
return FALSE;
@@ -11582,21 +11694,12 @@ BOOLEAN_T decode_native_get_opt(ErlNifEnv* env, ERL_NIF_TERM eVal,
return FALSE;
}
+ SGDBG( ("SOCKET", "decode_native_get_opt -> done\r\n") );
+
return TRUE;
}
-/*
-static
-void encode_bool(BOOLEAN_T val, ERL_NIF_TERM* eVal)
-{
- if (val)
- *eVal = esock_atom_true;
- else
- *eVal = esock_atom_false;
-}
-*/
-
/* +++ encode the ip socket option tos +++
* The (ip) option can be provide as:
@@ -12872,6 +12975,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
atom_association = MKA(env, str_association);
atom_assoc_id = MKA(env, str_assoc_id);
atom_authentication = MKA(env, str_authentication);
+ atom_bool = MKA(env, str_bool);
atom_close = MKA(env, str_close);
atom_closed = MKA(env, str_closed);
atom_closing = MKA(env, str_closing);
@@ -12886,6 +12990,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
atom_in6_sockaddr = MKA(env, str_in6_sockaddr);
atom_include = MKA(env, str_include);
atom_initial = MKA(env, str_initial);
+ atom_int = MKA(env, str_int);
atom_interface = MKA(env, str_interface);
atom_iow = MKA(env, str_iow);
atom_local_rwnd = MKA(env, str_local_rwnd);
@@ -12916,7 +13021,6 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
atom_peer_rwnd = MKA(env, str_peer_rwnd);
atom_peer_error = MKA(env, str_peer_error);
atom_probe = MKA(env, str_probe);
- atom_sec = MKA(env, str_sec);
atom_select = MKA(env, str_select);
atom_sender_dry = MKA(env, str_sender_dry);
atom_send_failure = MKA(env, str_send_failure);
@@ -12925,7 +13029,6 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
atom_sourceaddr = MKA(env, str_sourceaddr);
atom_timeout = MKA(env, str_timeout);
atom_true = MKA(env, str_true);
- atom_usec = MKA(env, str_usec);
atom_want = MKA(env, str_want);
/* Global atom(s) */
@@ -12968,11 +13071,14 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
esock_atom_rights = MKA(env, "rights");
esock_atom_scope_id = MKA(env, "scope_id");
esock_atom_sctp = MKA(env, "sctp");
+ esock_atom_sec = MKA(env, "sec");
esock_atom_seqpacket = MKA(env, "seqpacket");
+ esock_atom_socket = MKA(env, "socket");
esock_atom_spec_dst = MKA(env, "spec_dst");
esock_atom_stream = MKA(env, "stream");
esock_atom_tcp = MKA(env, "tcp");
esock_atom_throughput = MKA(env, "throughput");
+ esock_atom_timestamp = MKA(env, "timestamp");
esock_atom_tos = MKA(env, "tos");
esock_atom_true = MKA(env, "true");
esock_atom_trunc = MKA(env, "trunc");
@@ -12981,6 +13087,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
esock_atom_udp = MKA(env, "udp");
esock_atom_undefined = MKA(env, "undefined");
esock_atom_unknown = MKA(env, "unknown");
+ esock_atom_usec = MKA(env, "usec");
/* Global error codes */
esock_atom_eafnosupport = MKA(env, ESOCK_STR_EAFNOSUPPORT);
diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c
index 232e8200df..59cd1a3408 100644
--- a/erts/emulator/nifs/common/socket_util.c
+++ b/erts/emulator/nifs/common/socket_util.c
@@ -227,6 +227,11 @@ char* esock_encode_sockaddr(ErlNifEnv* env,
{
char* xres;
+ UDBG( ("SUTIL", "esock_encode_sockaddr -> entry with"
+ "\r\n family: %d"
+ "\r\n addrLen: %d"
+ "\r\n", sockAddrP->sa.sa_family, addrLen) );
+
switch (sockAddrP->sa.sa_family) {
case AF_INET:
xres = esock_encode_sockaddr_in4(env, &sockAddrP->in4, addrLen, eSockAddr);
@@ -860,6 +865,74 @@ char* esock_encode_ip6_address(ErlNifEnv* env,
+/* +++ esock_encode_timeval +++
+ *
+ * Encode a timeval struct into its erlang form, a map with two fields:
+ *
+ * sec
+ * usec
+ *
+ */
+extern
+char* esock_encode_timeval(ErlNifEnv* env,
+ struct timeval* timeP,
+ ERL_NIF_TERM* eTime)
+{
+ ERL_NIF_TERM keys[] = {esock_atom_sec, esock_atom_usec};
+ ERL_NIF_TERM vals[] = {MKL(env, timeP->tv_sec), MKL(env, timeP->tv_usec)};
+
+ unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM);
+ unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM);
+
+ ESOCK_ASSERT( (numKeys == numVals) );
+
+ if (!MKMA(env, keys, vals, numKeys, eTime))
+ return ESOCK_STR_EINVAL;
+
+ return NULL;
+}
+
+
+
+/* +++ esock_decode_timeval +++
+ *
+ * Decode a timeval in its erlang form (a map) into its native form,
+ * a timeval struct.
+ *
+ */
+extern
+char* esock_decode_timeval(ErlNifEnv* env,
+ ERL_NIF_TERM eTime,
+ struct timeval* timeP)
+{
+ ERL_NIF_TERM eSec, eUSec;
+ size_t sz;
+
+ // It must be a map
+ if (!IS_MAP(env, eTime))
+ return ESOCK_STR_EINVAL;
+
+ // It must have atleast two attributes
+ if (!enif_get_map_size(env, eTime, &sz) || (sz < 2))
+ return ESOCK_STR_EINVAL;
+
+ if (!GET_MAP_VAL(env, eTime, esock_atom_sec, &eSec))
+ return ESOCK_STR_EINVAL;
+
+ if (!GET_MAP_VAL(env, eTime, esock_atom_usec, &eUSec))
+ return ESOCK_STR_EINVAL;
+
+ if (!GET_LONG(env, eSec, &timeP->tv_sec))
+ return ESOCK_STR_EINVAL;
+
+ if (!GET_LONG(env, eUSec, &timeP->tv_usec))
+ return ESOCK_STR_EINVAL;
+
+ return NULL;
+}
+
+
+
/* +++ esock_decode_domain +++
*
* Decode the Erlang form of the 'domain' type, that is:
diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h
index 5d720936f3..22eed77d6e 100644
--- a/erts/emulator/nifs/common/socket_util.h
+++ b/erts/emulator/nifs/common/socket_util.h
@@ -110,6 +110,12 @@ char* esock_encode_ip6_address(ErlNifEnv* env,
ERL_NIF_TERM* eAddr);
#endif
+extern char* esock_encode_timeval(ErlNifEnv* env,
+ struct timeval* timeP,
+ ERL_NIF_TERM* eTime);
+extern char* esock_decode_timeval(ErlNifEnv* env,
+ ERL_NIF_TERM eTime,
+ struct timeval* timeP);
extern
char* esock_decode_domain(ErlNifEnv* env,
ERL_NIF_TERM eDomain,
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
index f6ca653fed..2219b1b271 100644
--- a/erts/preloaded/ebin/socket.beam
+++ b/erts/preloaded/ebin/socket.beam
Binary files differ
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
index b9d1705d45..5902c161db 100644
--- a/erts/preloaded/src/socket.erl
+++ b/erts/preloaded/src/socket.erl
@@ -90,12 +90,13 @@
raw_socket_option/0,
timeval/0,
- ip_tos_flag/0,
+ ip_tos/0,
ip_mreq/0,
ip_mreq_source/0,
ip_pmtudisc/0,
ip_msfilter_mode/0,
ip_msfilter/0,
+ ip_pktinfo/0,
ipv6_mreq/0,
ipv6_pmtudisc/0,
sctp_event_subscribe/0,
@@ -107,6 +108,9 @@
msghdr_flag/0,
msghdr_flags/0,
msghdr/0,
+ cmsghdr_level/0,
+ cmsghdr_type/0,
+ cmsghdr_data/0,
cmsghdr/0,
uint8/0,
@@ -168,11 +172,11 @@
usec := integer()}.
%% If the integer value is used its up to the caller to ensure its valid!
--type ip_tos_flag() :: lowdeley |
- throughput |
- reliability |
- mincost |
- integer().
+-type ip_tos() :: lowdeley |
+ throughput |
+ reliability |
+ mincost |
+ integer().
%% This type is used when requesting to become member of a multicast
%% group with a call to setopt. Example:
@@ -516,14 +520,32 @@
%% Only valid with recvmsg
flags => msghdr_flags()
}.
-%% At some point we should be able to encode/decode the most common types
-%% of control message headers. For now, we leave/take the data part raw
-%% (as a binary) and leave it to the user to figure out (how to encode/decode
-%% that bit).
+%% We are able to (completely) decode *some* control message headers.
+%% Even if we are able to decode both level and type, we may not be
+%% able to decode the data, in which case it will be a binary.
+-type ip_pktinfo() :: #{
+ ifindex => non_neg_integer(), % Interface Index
+ spec_dst => ip4_address(), % Local Address
+ addr => ip4_address() % Header Destination address
+ }.
+-type cmsghdr_level() :: socket | protocol() | integer().
+-type cmsghdr_type() :: timestamp |
+ rights |
+ credentials |
+ tos |
+ ttl |
+ origdstaddr |
+ integer().
+-type cmsghdr_data() :: timeval() | % if level = socket and type = timstamp
+ ip_pktinfo() | % if level = ip and type = pktinfo
+ ip_tos() | % if level = ip and type = tos
+ integer() | % if level = ip and type = ttl
+ sockaddr_in4() | % if level = ip and type = origdstaddr
+ binary().
-type cmsghdr() :: #{
- level => protocol() | integer(),
- type => integer(),
- data => binary()
+ level => cmsghdr_level(),
+ type => cmsghdr_type(),
+ data => cmsghdr_data()
}.
-define(SOCKET_DOMAIN_LOCAL, 1).
@@ -1808,6 +1830,10 @@ do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Timeout) ->
{error, timeout}
end;
+ {error, closed} = ERROR ->
+ do_close(SockRef),
+ ERROR;
+
{error, _Reason} = ERROR ->
ERROR