aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/nifs/common/socket_nif.c
diff options
context:
space:
mode:
authorMicael Karlberg <[email protected]>2018-05-04 16:00:26 +0200
committerMicael Karlberg <[email protected]>2018-09-18 13:01:37 +0200
commit8e14247bc5faf5abf67901b6ae5028f2b1897c65 (patch)
treed2b1f37fcd880862d99a92f699a197387a3b727d /erts/emulator/nifs/common/socket_nif.c
parentb69436fc5970ff8fc749a37351e9d9c5a54f445e (diff)
downloadotp-8e14247bc5faf5abf67901b6ae5028f2b1897c65.tar.gz
otp-8e14247bc5faf5abf67901b6ae5028f2b1897c65.tar.bz2
otp-8e14247bc5faf5abf67901b6ae5028f2b1897c65.zip
[socket-nif] Preliminary setopt
*Very* partial setopt implementation.
Diffstat (limited to 'erts/emulator/nifs/common/socket_nif.c')
-rw-r--r--erts/emulator/nifs/common/socket_nif.c1029
1 files changed, 992 insertions, 37 deletions
diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c
index fdae18a513..532ac5c211 100644
--- a/erts/emulator/nifs/common/socket_nif.c
+++ b/erts/emulator/nifs/common/socket_nif.c
@@ -59,6 +59,7 @@
#include <ctype.h>
#include <sys/types.h>
#include <errno.h>
+#include <netinet/ip.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
@@ -338,6 +339,26 @@ typedef union {
#define SOCKET_SHUTDOWN_HOW_RDWR 2
+#define SOCKET_OPT_LEVEL_OTP 0
+#define SOCKET_OPT_LEVEL_SOCKET 1
+#define SOCKET_OPT_LEVEL_IP 2
+#define SOCKET_OPT_LEVEL_IPV6 3
+#define SOCKET_OPT_LEVEL_TCP 4
+#define SOCKET_OPT_LEVEL_UDP 5
+#define SOCKET_OPT_LEVEL_SCTP 6
+
+#define SOCKET_OPT_OTP_DEBUG 0
+#define SOCKET_OPT_OTP_IOW 1
+#define SOCKET_OPT_SOCK_KEEPALIVE 0
+#define SOCKET_OPT_SOCK_LINGER 1
+#define SOCKET_OPT_IP_RECVTOS 0
+#define SOCKET_OPT_IP_ROUTER_ALERT 1
+#define SOCKET_OPT_IP_TOS 2
+#define SOCKET_OPT_IP_TTL 3
+#define SOCKET_OPT_IPV6_HOPLIMIT 0
+#define SOCKET_OPT_TCP_MAXSEG 0
+
+
/* =================================================================== *
* *
* Various enif macros *
@@ -407,7 +428,7 @@ typedef union {
#define sock_connect(s, addr, len) connect((s), (addr), (len))
#define sock_create_event(s) WSACreateEvent()
#define sock_errno() WSAGetLastError()
-#define sock_getopt(s,t,n,v,l) getsockopt((s),(t),(n),(v),(l))
+#define sock_getopt(s,l,o,v,ln) getsockopt((s),(l),(o),(v),(ln))
#define sock_htons(x) htons((x))
#define sock_htonl(x) htonl((x))
#define sock_listen(s, b) listen((s), (b))
@@ -421,6 +442,7 @@ typedef union {
#define sock_send(s,buf,len,flag) send((s),(buf),(len),(flag))
#define sock_sendto(s,buf,blen,flag,addr,alen) \
sendto((s),(buf),(blen),(flag),(addr),(alen))
+#define sock_setopt(s,l,o,v,ln) setsockopt((s),(l),(o),(v),(ln))
#define sock_shutdown(s, how) shutdown((s), (how))
@@ -458,6 +480,7 @@ static unsigned long one_value = 1;
#define sock_send(s,buf,len,flag) send((s), (buf), (len), (flag))
#define sock_sendto(s,buf,blen,flag,addr,alen) \
sendto((s),(buf),(blen),(flag),(addr),(alen))
+#define sock_setopt(s,l,o,v,ln) setsockopt((s),(l),(o),(v),(ln))
#define sock_shutdown(s, how) shutdown((s), (how))
#endif /* !__WIN32__ */
@@ -570,6 +593,27 @@ typedef struct {
} SocketDescriptor;
+#define SOCKET_OPT_VALUE_UNDEF 0
+#define SOCKET_OPT_VALUE_BOOL 1
+#define SOCKET_OPT_VALUE_INT 2
+#define SOCKET_OPT_VALUE_LINGER 3
+#define SOCKET_OPT_VALUE_BIN 4
+
+typedef struct {
+ unsigned int tag;
+ union {
+ BOOLEAN_T boolVal;
+ int intVal;
+ struct linger lingerVal;
+ ErlNifBinary binVal;
+ } u;
+ /*
+ void* optValP; // Points to the actual data (above)
+ socklen_t optValLen; // The size of the option value
+ */
+} SocketOptValue;
+
+
/* Global stuff (do we really need to "collect"
* these things?)
*/
@@ -650,12 +694,12 @@ static ERL_NIF_TERM nif_close(ErlNifEnv* env,
static ERL_NIF_TERM nif_shutdown(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[]);
-static ERL_NIF_TERM nif_setsockopt(ErlNifEnv* env,
- int argc,
- const ERL_NIF_TERM argv[]);
-static ERL_NIF_TERM nif_getsockopt(ErlNifEnv* env,
- int argc,
- const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM nif_setopt(ErlNifEnv* env,
+ int argc,
+ const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM nif_getopt(ErlNifEnv* env,
+ int argc,
+ const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM nif_finalize_connection(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[]);
@@ -718,6 +762,22 @@ static ERL_NIF_TERM nclose(ErlNifEnv* env,
static ERL_NIF_TERM nshutdown(ErlNifEnv* env,
SocketDescriptor* descP,
int how);
+static ERL_NIF_TERM nsetopt(ErlNifEnv* env,
+ SocketDescriptor* descP,
+ BOOLEAN_T isEncoded,
+ BOOLEAN_T isOTP,
+ int level,
+ int opt,
+ SocketOptValue* valP);
+static ERL_NIF_TERM nsetopt_otp(ErlNifEnv* env,
+ SocketDescriptor* descP,
+ int opt,
+ SocketOptValue* valP);
+static ERL_NIF_TERM nsetopt_gen(ErlNifEnv* env,
+ SocketDescriptor* descP,
+ int level,
+ int opt,
+ SocketOptValue* valP);
static ERL_NIF_TERM send_check_result(ErlNifEnv* env,
SocketDescriptor* descP,
@@ -789,6 +849,15 @@ static void encode_address(ErlNifEnv* env,
unsigned int fromAddrLen,
ERL_NIF_TERM* fromDomainT,
ERL_NIF_TERM* fromSourceT);
+static BOOLEAN_T decode_sock_linger(ErlNifEnv* env,
+ ERL_NIF_TERM eVal,
+ struct linger* valP);
+static BOOLEAN_T decode_ip_tos(ErlNifEnv* env,
+ ERL_NIF_TERM eVal,
+ int* val);
+static BOOLEAN_T decode_bool(ErlNifEnv* env,
+ ERL_NIF_TERM eVal,
+ BOOLEAN_T* val);
static void inform_waiting_procs(ErlNifEnv* env,
SocketDescriptor* descP,
@@ -796,6 +865,12 @@ static void inform_waiting_procs(ErlNifEnv* env,
BOOLEAN_T free,
ERL_NIF_TERM reason);
+static int socket_setopt(int sock,
+ int level,
+ int opt,
+ const void* optVal,
+ const socklen_t optLen);
+
static BOOLEAN_T verify_is_connected(SocketDescriptor* descP, int* err);
static SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event);
@@ -812,6 +887,62 @@ static BOOLEAN_T eproto2proto(int eproto, int* proto);
static BOOLEAN_T ehow2how(unsigned int ehow, int* how);
static BOOLEAN_T esendflags2sendflags(unsigned int esendflags, int* sendflags);
static BOOLEAN_T erecvflags2recvflags(unsigned int erecvflags, int* recvflags);
+static BOOLEAN_T elevel2level(BOOLEAN_T isEncoded,
+ int eLevel,
+ BOOLEAN_T* isOTP,
+ int* level);
+static BOOLEAN_T eoptval2optval(ErlNifEnv* env,
+ BOOLEAN_T isEncoded,
+ BOOLEAN_T isOTP,
+ int level,
+ int eOpt,
+ ERL_NIF_TERM eVal,
+ int* opt,
+ SocketOptValue* val);
+static BOOLEAN_T eoptval2optval_otp(ErlNifEnv* env,
+ int eOpt,
+ ERL_NIF_TERM eVal,
+ int* opt,
+ SocketOptValue* valP);
+static BOOLEAN_T eoptval2optval_plain(ErlNifEnv* env,
+ int eOpt,
+ ERL_NIF_TERM eVal,
+ int* opt,
+ SocketOptValue* valP);
+static BOOLEAN_T eoptval2optval_socket(ErlNifEnv* env,
+ int eOpt,
+ ERL_NIF_TERM eVal,
+ int* opt,
+ SocketOptValue* valP);
+static BOOLEAN_T eoptval2optval_ip(ErlNifEnv* env,
+ int eOpt,
+ ERL_NIF_TERM eVal,
+ int* opt,
+ SocketOptValue* valP);
+#if defined(SOL_IPV6)
+static BOOLEAN_T eoptval2optval_ipv6(ErlNifEnv* env,
+ int eOpt,
+ ERL_NIF_TERM eVal,
+ int* opt,
+ SocketOptValue* valP);
+#endif
+static BOOLEAN_T eoptval2optval_tcp(ErlNifEnv* env,
+ int eOpt,
+ ERL_NIF_TERM eVal,
+ int* opt,
+ SocketOptValue* valP);
+static BOOLEAN_T eoptval2optval_udp(ErlNifEnv* env,
+ int eOpt,
+ ERL_NIF_TERM eVal,
+ int* opt,
+ SocketOptValue* valP);
+#ifdef HAVE_SCTP
+static BOOLEAN_T eoptval2optval_sctp(ErlNifEnv* env,
+ int eOpt,
+ ERL_NIF_TERM eVal,
+ int* opt,
+ SocketOptValue* valP);
+#endif
#ifdef HAVE_SETNS
static BOOLEAN_T emap2netns(ErlNifEnv* env, ERL_NIF_TERM map, char** netns);
static BOOLEAN_T change_network_namespace(char* netns, int* cns, int* err);
@@ -989,8 +1120,8 @@ static SocketData socketData;
*
* And some functions to manipulate and retrieve socket options:
* -------------------------------------------------------------
- * nif_setsockopt/3
- * nif_getsockopt/2
+ * nif_setopt/3
+ * nif_getopt/2
*
* And some socket admin functions:
* -------------------------------------------------------------
@@ -2829,6 +2960,697 @@ ERL_NIF_TERM nshutdown(ErlNifEnv* env,
}
+
+
+/* ----------------------------------------------------------------------
+ * nif_setopt
+ *
+ * Description:
+ * Set socket option.
+ * Its possible to use a "raw" mode (not encoded). That is, we do not
+ * interpret level, opt and value. They are passed "as is" to the
+ * setsockopt function call (the value arguments is assumed to be a
+ * binary, already encoded).
+ *
+ * Arguments:
+ * Socket (ref) - Points to the socket descriptor.
+ * Encoded - Are the "arguments" encoded or not.
+ * Level - Level of the socket option.
+ * Opt - The socket option.
+ * Value - Value of the socket option (type depend on the option).
+ */
+
+static
+ERL_NIF_TERM nif_setopt(ErlNifEnv* env,
+ int argc,
+ const ERL_NIF_TERM argv[])
+{
+ SocketDescriptor* descP;
+ unsigned int eIsEncoded;
+ BOOLEAN_T isEncoded, isOTP;
+ int eLevel, level = -1;
+ int eOpt, opt = -1;
+ ERL_NIF_TERM eVal;
+ SocketOptValue val;
+
+ if ((argc != 5) ||
+ !enif_get_resource(env, argv[0], sockets, (void**) &descP) ||
+ !GET_UINT(env, argv[1], &eIsEncoded) ||
+ !GET_INT(env, argv[2], &eLevel) ||
+ !GET_INT(env, argv[3], &eOpt)) {
+ return enif_make_badarg(env);
+ }
+ eVal = argv[4];
+
+ isEncoded = ((eIsEncoded == 0) ? FALSE : TRUE);
+
+ if (!elevel2level(isEncoded, eLevel, &isOTP, &level))
+ return make_error(env, atom_einval);
+
+ if (!eoptval2optval(env, isEncoded, isOTP, level, eOpt, eVal, &opt, &val))
+ return make_error(env, atom_einval);
+
+ return nsetopt(env, descP, isEncoded, isOTP, level, opt, &val);
+}
+
+
+static
+ERL_NIF_TERM nsetopt(ErlNifEnv* env,
+ SocketDescriptor* descP,
+ BOOLEAN_T isEncoded,
+ BOOLEAN_T isOTP,
+ int level,
+ int opt,
+ SocketOptValue* valP)
+{
+ ERL_NIF_TERM result;
+ int res;
+
+ if (!isEncoded) {
+ res = socket_setopt(descP->sock, level, opt,
+ valP->u.binVal.data, valP->u.binVal.size);
+ if (res != 0)
+ result = make_error2(env, res);
+ else
+ result = atom_ok;
+ } else {
+ if (isOTP) {
+ /* These are not actual socket options,
+ * but options for our implementation.
+ */
+ result = nsetopt_otp(env, descP, opt, valP);
+ } else {
+ /* Basically, call setsockopt(...)
+ * <KOLLA>
+ * How do we know what type each option have? tag in value type?
+ * </KOLLA>
+ */
+ result = nsetopt_gen(env, descP, level, opt, valP);
+ }
+ }
+
+ return result;
+}
+
+
+static
+ERL_NIF_TERM nsetopt_otp(ErlNifEnv* env,
+ SocketDescriptor* descP,
+ int opt,
+ SocketOptValue* valP)
+{
+ ERL_NIF_TERM result;
+
+ /* Make an idiot check just to be on the safe side... */
+ if (valP->tag == SOCKET_OPT_VALUE_UNDEF)
+ return make_error(env, atom_einval);
+
+ switch (opt) {
+ case SOCKET_OPT_OTP_DEBUG:
+ descP->dbg = valP->u.boolVal;
+ result = atom_ok;
+ break;
+
+ case SOCKET_OPT_OTP_IOW:
+ descP->iow = valP->u.boolVal;
+ result = atom_ok;
+ break;
+
+ default:
+ result = make_error(env, atom_einval);
+ break;
+ }
+
+ return result;
+}
+
+
+static
+ERL_NIF_TERM nsetopt_gen(ErlNifEnv* env,
+ SocketDescriptor* descP,
+ int level,
+ int opt,
+ SocketOptValue* valP)
+{
+ socklen_t optLen;
+ int res;
+ ERL_NIF_TERM result;
+
+ switch (valP->tag) {
+ case SOCKET_OPT_VALUE_INT:
+ {
+ optLen = sizeof(valP->u.intVal);
+ res = socket_setopt(descP->sock, level, opt,
+ (void*) &valP->u.intVal, optLen);
+ if (res != 0)
+ result = make_error2(env, res);
+ else
+ result = atom_ok;
+ }
+ break;
+
+ case SOCKET_OPT_VALUE_BIN:
+ {
+ optLen = valP->u.binVal.size;
+ res = socket_setopt(descP->sock, level, opt,
+ &valP->u.binVal.data, optLen);
+ if (res != 0)
+ result = make_error2(env, res);
+ else
+ result = atom_ok;
+ }
+ break;
+
+ default:
+ result = make_error(env, atom_einval);
+ }
+
+ return result;
+}
+
+
+
+static
+BOOLEAN_T elevel2level(BOOLEAN_T isEncoded,
+ int eLevel,
+ BOOLEAN_T* isOTP,
+ int* level)
+{
+ BOOLEAN_T result;
+
+ if (isEncoded) {
+ switch (eLevel) {
+ case SOCKET_OPT_LEVEL_OTP:
+ *isOTP = TRUE;
+ *level = -1;
+ result = TRUE;
+ break;
+
+ case SOCKET_OPT_LEVEL_SOCKET:
+ *isOTP = FALSE;
+ *level = SOL_SOCKET;
+ result = TRUE;
+ break;
+
+ case SOCKET_OPT_LEVEL_IP:
+ *isOTP = FALSE;
+#if defined(SOL_IP)
+ *level = SOL_IP;
+#else
+ *level = IPROTO_IP;
+#endif
+ result = TRUE;
+ break;
+
+#if defined(SOL_IPV6)
+ case SOCKET_OPT_LEVEL_IPV6:
+ *isOTP = FALSE;
+ *level = SOL_IPV6;
+ result = TRUE;
+ break;
+#endif
+
+ case SOCKET_OPT_LEVEL_TCP:
+ *isOTP = FALSE;
+ *level = IPPROTO_TCP;
+ result = TRUE;
+ break;
+
+ case SOCKET_OPT_LEVEL_UDP:
+ *isOTP = FALSE;
+ *level = IPPROTO_UDP;
+ result = TRUE;
+ break;
+
+#ifdef HAVE_SCTP
+ case SOCKET_OPT_LEVEL_SCTP:
+ *isOTP = FALSE;
+ *level = IPPROTO_SCTP;
+ result = TRUE;
+ break;
+#endif
+
+ default:
+ *isOTP = FALSE;
+ *level = -1;
+ result = FALSE;
+ break;
+ }
+ } else {
+ *isOTP = FALSE;
+ *level = eLevel;
+ result = TRUE;
+ }
+
+ return result;
+}
+
+
+static
+BOOLEAN_T eoptval2optval(ErlNifEnv* env,
+ BOOLEAN_T isEncoded,
+ BOOLEAN_T isOTP,
+ int level,
+ int eOpt,
+ ERL_NIF_TERM eVal,
+ int* opt,
+ SocketOptValue* valP)
+{
+ if (isOTP) {
+ return eoptval2optval_otp(env, eOpt, eVal, opt, valP);
+ } else if (!isEncoded) {
+ return eoptval2optval_plain(env, eOpt, eVal, opt, valP);
+ } else {
+ switch (level) {
+ case SOL_SOCKET:
+ return eoptval2optval_socket(env, eOpt, eVal, opt, valP);
+ break;
+
+#if defined(SOL_IP)
+ case SOL_IP:
+#else
+ case IPPROTO_IP:
+#endif
+ return eoptval2optval_ip(env, eOpt, eVal, opt, valP);
+ break;
+
+#if defined(SOL_IPV6)
+ case SOL_IPV6:
+ return eoptval2optval_ipv6(env, eOpt, eVal, opt, valP);
+ break;
+#endif
+
+ case IPPROTO_TCP:
+ return eoptval2optval_tcp(env, eOpt, eVal, opt, valP);
+ break;
+
+ case IPPROTO_UDP:
+ return eoptval2optval_udp(env, eOpt, eVal, opt, valP);
+ break;
+
+#ifdef HAVE_SCTP
+ case IPPROTO_SCTP:
+ return eoptval2optval_sctp(env, eOpt, eVal, opt, valP);
+ break;
+#endif
+
+ default:
+ *opt = -1;
+ return FALSE;
+ }
+ }
+}
+
+
+
+static
+BOOLEAN_T eoptval2optval_otp(ErlNifEnv* env,
+ int eOpt,
+ ERL_NIF_TERM eVal,
+ int* opt,
+ SocketOptValue* valP)
+{
+ BOOLEAN_T result = FALSE;
+
+ switch (eOpt) {
+ case SOCKET_OPT_OTP_IOW:
+ case SOCKET_OPT_OTP_DEBUG:
+ {
+ if (decode_bool(env, eVal, &valP->u.boolVal)) {
+ valP->tag = SOCKET_OPT_VALUE_BOOL;
+ result = TRUE;
+ } else {
+ result = FALSE;
+ }
+ *opt = eOpt;
+ }
+ break;
+
+ default:
+ *opt = -1;
+ valP->tag = SOCKET_OPT_VALUE_UNDEF;
+ }
+
+ return result;
+}
+
+
+static
+BOOLEAN_T eoptval2optval_plain(ErlNifEnv* env,
+ int eOpt,
+ ERL_NIF_TERM eVal,
+ int* opt,
+ SocketOptValue* valP)
+{
+ if (!GET_BIN(env, eVal, &valP->u.binVal))
+ return FALSE;
+ valP->tag = SOCKET_OPT_VALUE_BIN;
+ *opt = eOpt;
+
+ return TRUE;
+}
+
+
+
+static
+BOOLEAN_T eoptval2optval_socket(ErlNifEnv* env,
+ int eOpt,
+ ERL_NIF_TERM eVal,
+ int* opt,
+ SocketOptValue* valP)
+{
+ switch (eOpt) {
+#if defined(SO_KEEPALIVE)
+ case SOCKET_OPT_SOCK_KEEPALIVE:
+ {
+ BOOLEAN_T val;
+
+ if (decode_bool(env, eVal, &val)) {
+ *opt = SO_KEEPALIVE;
+ valP->tag = SOCKET_OPT_VALUE_INT;
+ valP->u.intVal = (val) ? 1 : 0;
+ return TRUE;
+ } else {
+ *opt = -1;
+ valP->tag = SOCKET_OPT_VALUE_UNDEF;
+ return FALSE;
+ }
+ }
+ break;
+#endif
+
+#if defined(SO_LINGER)
+ case SOCKET_OPT_SOCK_LINGER:
+ {
+ if (decode_sock_linger(env, eVal, &valP->u.lingerVal)) {
+ *opt = SO_LINGER;
+ valP->tag = SOCKET_OPT_VALUE_LINGER;
+ return TRUE;
+ } else {
+ *opt = -1;
+ valP->tag = SOCKET_OPT_VALUE_UNDEF;
+ return FALSE;
+ }
+ }
+ break;
+#endif
+
+ default:
+ *opt = -1;
+ valP->tag = SOCKET_OPT_VALUE_UNDEF;
+ return FALSE;
+ }
+}
+
+
+
+static
+BOOLEAN_T eoptval2optval_ip(ErlNifEnv* env,
+ int eOpt,
+ ERL_NIF_TERM eVal,
+ int* opt,
+ SocketOptValue* valP)
+{
+ switch (eOpt) {
+#if defined(IP_RECVTOS)
+ case SOCKET_OPT_IP_RECVTOS:
+ {
+ BOOLEAN_T val;
+
+ if (decode_bool(env, eVal, &val)) {
+ *opt = IP_RECVTOS;
+ valP->tag = SOCKET_OPT_VALUE_INT;
+ valP->u.intVal = (val) ? 1 : 0;
+ return TRUE;
+ } else {
+ *opt = -1;
+ valP->tag = SOCKET_OPT_VALUE_UNDEF;
+ return TRUE;
+ }
+ }
+ break;
+#endif
+
+#if defined(IP_ROUTER_ALERT)
+ case SOCKET_OPT_IP_ROUTER_ALERT:
+ if (GET_INT(env, eVal, &valP->u.intVal)) {
+ valP->tag = SOCKET_OPT_VALUE_INT;
+ *opt = IP_ROUTER_ALERT;
+ return TRUE;
+ } else {
+ *opt = -1;
+ valP->tag = SOCKET_OPT_VALUE_UNDEF;
+ return FALSE;
+ }
+ break;
+#endif
+
+#if defined(IP_TOS)
+ case SOCKET_OPT_IP_TOS:
+ {
+ if (decode_ip_tos(env, eVal, &valP->u.intVal)) {
+ valP->tag = SOCKET_OPT_VALUE_INT;
+ *opt = IP_TOS;
+ return TRUE;
+ } else {
+ *opt = -1;
+ return FALSE;
+ }
+ }
+ break;
+#endif
+
+#if defined(IP_TTL)
+ case SOCKET_OPT_IP_TTL:
+ /* <KOLLA>
+ * Should we care about the value? That is, if it is valid?
+ * And what is the valid range anyway for ttl? 0 - 255?
+ * </KOLLA>
+ */
+ if (!GET_INT(env, eVal, &valP->u.intVal))
+ return FALSE; // PLACEHOLDER - We should really be more informative
+ valP->tag = SOCKET_OPT_VALUE_INT;
+ *opt = IP_TTL;
+ return TRUE;
+ break;
+#endif
+
+ default:
+ *opt = -1;
+ valP->tag = SOCKET_OPT_VALUE_UNDEF;
+ return FALSE;
+ }
+
+}
+
+
+
+#if defined(SOL_IPV6)
+static
+BOOLEAN_T eoptval2optval_ipv6(ErlNifEnv* env,
+ int eOpt,
+ ERL_NIF_TERM eVal,
+ int* opt,
+ SocketOptValue* valP)
+{
+ BOOLEAN_T result = FALSE;
+
+ switch (eOpt) {
+#if defined(IPV6_HOPLIMIT)
+ case SOCKET_OPT_IPV6_HOPLIMIT:
+ {
+ BOOLEAN_T val;
+
+ if (decode_bool(env, eVal, &val)) {
+ valP->tag = SOCKET_OPT_VALUE_INT;
+ valP->u.intVal = (val) ? 1 : 0;
+ *opt = IPV6_HOPLIMIT;
+ result = TRUE;
+ } else {
+ *opt = -1;
+ result = FALSE;
+ }
+ }
+ break;
+#endif
+
+ default:
+ *opt = -1;
+ valP->tag = SOCKET_OPT_VALUE_UNDEF;
+ result = FALSE;
+ break;
+ }
+
+ return result;
+}
+#endif
+
+
+
+static
+BOOLEAN_T eoptval2optval_tcp(ErlNifEnv* env,
+ int eOpt,
+ ERL_NIF_TERM eVal,
+ int* opt,
+ SocketOptValue* valP)
+{
+ switch (eOpt) {
+#if defined(TCP_MAXSEG)
+ case SOCKET_OPT_TCP_MAXSEG:
+ if (!GET_INT(env, eVal, &valP->u.intVal)) {
+ valP->tag = SOCKET_OPT_VALUE_INT;
+ *opt = TCP_MAXSEG;
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+ break;
+#endif
+
+ default:
+ *opt = -1;
+ valP->tag = SOCKET_OPT_VALUE_UNDEF;
+ return FALSE;
+ }
+}
+
+
+
+/* +++ decode UDP socket options +++
+ * Currently there are no such options, so this function
+ * is just a placeholder!
+ */
+static
+BOOLEAN_T eoptval2optval_udp(ErlNifEnv* env,
+ int eOpt,
+ ERL_NIF_TERM eVal,
+ int* opt,
+ SocketOptValue* valP)
+{
+ switch (eOpt) {
+ default:
+ *opt = -1;
+ valP->tag = SOCKET_OPT_VALUE_UNDEF;
+ return FALSE;
+ }
+}
+
+
+
+#ifdef HAVE_SCTP
+static
+BOOLEAN_T eoptval2optval_sctp(ErlNifEnv* env,
+ int eOpt,
+ ERL_NIF_TERM eVal,
+ int* opt,
+ SocketOptValue* valP)
+{
+ switch (eOpt) {
+#if defined(SCTP_AUTOCLOSE)
+ case SOCKET_OPT_SCTP_AUTOCLOSE:
+ if (!GET_INT(env, eVal, &valP->u.intVal))
+ return FALSE; // PLACEHOLDER - We should really be more informative
+ valP->tag = SOCKET_OPT_VALUE_INT;
+ *opt = SCTP_AUTOCLOSE;
+ return TRUE;
+ break;
+#endif
+
+ default:
+ *opt = -1;
+ valP->tag = SOCKET_OPT_VALUE_UNDEF;
+ return FALSE;
+ }
+}
+#endif
+
+
+
+/* +++ socket_setopt +++
+ *
+ * <Per H @ Tail-f>
+ * The original code here had problems that possibly
+ * only occur if you abuse it for non-INET sockets, but anyway:
+ * a) If the getsockopt for SO_PRIORITY or IP_TOS failed, the actual
+ * requested setsockopt was never even attempted.
+ * b) If {get,set}sockopt for one of IP_TOS and SO_PRIORITY failed,
+ * but ditto for the other worked and that was actually the requested
+ * option, failure was still reported to erlang.
+ * </Per H @ Tail-f>
+ *
+ * <PaN>
+ * The relations between SO_PRIORITY, TOS and other options
+ * is not what you (or at least I) would expect...:
+ * If TOS is set after priority, priority is zeroed.
+ * If any other option is set after tos, tos might be zeroed.
+ * Therefore, save tos and priority. If something else is set,
+ * restore both after setting, if tos is set, restore only
+ * prio and if prio is set restore none... All to keep the
+ * user feeling socket options are independent.
+ * </PaN>
+ */
+static
+int socket_setopt(int sock, int level, int opt,
+ const void* optVal, const socklen_t optLen)
+{
+ int res;
+
+#if defined(IP_TOS) && defined(SOL_IP) && defined(SO_PRIORITY)
+ int tmpIValPRIO;
+ int tmpIValTOS;
+ int resPRIO;
+ int resTOS;
+ SOCKLEN_T tmpArgSzPRIO = sizeof(tmpIValPRIO);
+ SOCKLEN_T tmpArgSzTOS = sizeof(tmpIValTOS);
+
+ resPRIO = sock_getopt(sock, SOL_SOCKET, SO_PRIORITY,
+ &tmpIValPRIO, &tmpArgSzPRIO);
+ resTOS = sock_getopt(sock, SOL_IP, IP_TOS,
+ &tmpIValTOS, &tmpArgSzTOS);
+
+ res = sock_setopt(sock, level, opt, optVal, optLen);
+ if (res == 0) {
+
+ /* Ok, now we *maybe* need to "maybe" restore PRIO and TOS...
+ * maybe, possibly, ...
+ */
+
+ if (opt != SO_PRIORITY) {
+ if ((opt != IP_TOS) && (resTOS == 0)) {
+ resTOS = sock_setopt(sock, SOL_IP, IP_TOS,
+ (void *) &tmpIValTOS,
+ tmpArgSzTOS);
+ res = resTOS;
+ }
+ if ((res == 0) && (resPRIO == 0)) {
+ resPRIO = sock_setopt(sock, SOL_SOCKET, SO_PRIORITY,
+ &tmpIValPRIO,
+ tmpArgSzPRIO);
+
+ /* Some kernels set a SO_PRIORITY by default
+ * that you are not permitted to reset,
+ * silently ignore this error condition.
+ */
+
+ if ((resPRIO != 0) && (sock_errno() == EPERM)) {
+ res = 0;
+ } else {
+ res = resPRIO;
+ }
+ }
+ }
+ }
+
+#else
+
+ res = sock_setopt(sock, level, opt, optVal, optLen);
+
+#endif
+
+ return res;
+}
+
+
/* ----------------------------------------------------------------------
* U t i l i t y F u n c t i o n s
* ----------------------------------------------------------------------
@@ -3468,6 +4290,139 @@ char* decode_address_atom(ErlNifEnv* env,
}
+static
+BOOLEAN_T decode_bool(ErlNifEnv* env, ERL_NIF_TERM eVal, BOOLEAN_T* val)
+{
+ unsigned int len;
+ char b[16]; // Just in case...
+
+ /* Verify that the value is actually an atom */
+ if (!IS_ATOM(env, eVal))
+ return FALSE;
+
+ /* Verify that the value is of acceptable length */
+ if (!(GET_ATOM_LEN(env, eVal, &len) &&
+ (len > 0) &&
+ (len <= sizeof("false"))))
+ return FALSE;
+
+ /* And finally try to extract the value */
+ if (!GET_ATOM(env, eVal, b, sizeof(b)))
+ return FALSE;
+
+ if (strncmp(b, "true", len) == 0)
+ *val = TRUE;
+ else
+ *val = FALSE;
+
+ return TRUE;
+}
+
+
+
+/* +++ decode the linger value +++
+ * The (socket) linger option is provided as a two tuple:
+ *
+ * {OnOff :: boolean(), Time :: integer()}
+ *
+ */
+static
+BOOLEAN_T decode_sock_linger(ErlNifEnv* env, ERL_NIF_TERM eVal, struct linger* valP)
+{
+ const ERL_NIF_TERM* lt; // The array of the elements of the tuple
+ int sz; // The size of the tuple - should be 2
+ BOOLEAN_T onOff;
+ int secs;
+
+ if (!GET_TUPLE(env, eVal, &sz, &lt))
+ return FALSE;
+
+ if (sz != 2)
+ return FALSE;
+
+
+ /* So fas so good - now check the two elements of the tuple. */
+
+ if (!decode_bool(env, lt[0], &onOff))
+ return FALSE;
+
+ if (!GET_INT(env, lt[1], &secs))
+ return FALSE;
+
+ valP->l_onoff = (onOff) ? 1 : 0;
+ valP->l_linger = secs;
+
+ return TRUE;
+}
+
+
+
+/* +++ decocde the ip socket option tos +++
+ * The (ip) option can be provide in two ways:
+ *
+ * atom() | integer()
+ *
+ * When its an atom it can have the values:
+ *
+ * lowdelay | throughput | reliability | mincost
+ *
+ */
+static
+BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val)
+{
+ BOOLEAN_T result = FALSE;
+
+ if (IS_ATOM(env, eVal)) {
+ unsigned int len;
+ char b[sizeof("reliability")+1]; // Just in case...
+
+ if (!(GET_ATOM_LEN(env, eVal, &len) &&
+ (len > 0) &&
+ (len <= (sizeof("reliability"))))) {
+ *val = -1;
+ return FALSE;
+ }
+
+ if (!GET_ATOM(env, eVal, b, sizeof(b))) {
+ *val = -1;
+ return FALSE;
+ }
+
+ if (strncmp(b, "lowdelay", len) == 0) {
+ *val = IPTOS_LOWDELAY;
+ result = TRUE;
+ } else if (strncmp(b, "throughput", len) == 0) {
+ *val = IPTOS_THROUGHPUT;
+ result = TRUE;
+ } else if (strncmp(b, "reliability", len) == 0) {
+ *val = IPTOS_RELIABILITY;
+ result = TRUE;
+ } else if (strncmp(b, "mincost", len) == 0) {
+ *val = IPTOS_MINCOST;
+ result = TRUE;
+ } else {
+ *val = -1;
+ result = FALSE;
+ }
+
+ } else if (IS_NUM(env, eVal)) {
+
+ if (GET_INT(env, eVal, val)) {
+ result = TRUE;
+ } else {
+ *val = -1;
+ result = FALSE;
+ }
+
+ } else {
+ *val = -1;
+ result = FALSE;
+ }
+
+ return result;
+}
+
+
/* *** alloc_descriptor ***
* Allocate and perform basic initialization of a socket descriptor.
@@ -4232,8 +5187,8 @@ ErlNifFunc socket_funcs[] =
{"nif_recvfrom", 2, nif_recvfrom, 0},
{"nif_close", 1, nif_close, 0},
{"nif_shutdown", 2, nif_shutdown, 0},
- {"nif_setsockopt", 3, nif_setsockopt, 0},
- {"nif_getsockopt", 2, nif_getsockopt, 0},
+ {"nif_setopt", 3, nif_setopt, 0},
+ {"nif_getopt", 2, nif_getopt, 0},
/* "Extra" functions to "complete" the socket interface.
* For instance, the function nif_finalize_connection
@@ -4260,38 +5215,38 @@ BOOLEAN_T extract_item_on_load(ErlNifEnv* env,
return TRUE;
}
- static
- BOOLEAN_T extract_debug_on_load(ErlNifEnv* env, ERL_NIF_TERM map, BOOLEAN_T def)
- {
- ERL_NIF_TERM dbgKey = enif_make_atom(env, "debug");
- ERL_NIF_TERM dbgVal;
- unsigned int len;
- char d[16]; // Just in case...
+static
+BOOLEAN_T extract_debug_on_load(ErlNifEnv* env, ERL_NIF_TERM map, BOOLEAN_T def)
+{
+ ERL_NIF_TERM dbgKey = enif_make_atom(env, "debug");
+ ERL_NIF_TERM dbgVal;
+ unsigned int len;
+ char d[16]; // Just in case...
- /* Extra the value of the debug property */
- if (!extract_item_on_load(env, map, dbgKey, &dbgVal))
- return def;
+ /* Extra the value of the debug property */
+ if (!extract_item_on_load(env, map, dbgKey, &dbgVal))
+ return def;
- /* Verify that the value is actually an atom */
- if (!enif_is_atom(env, dbgVal))
- return def;
+ /* Verify that the value is actually an atom */
+ if (!enif_is_atom(env, dbgVal))
+ return def;
- /* Verify that the value is of acceptable length */
- if (!(GET_ATOM_LEN(env, dbgVal, &len) &&
- (len > 0) &&
- (len <= sizeof("false"))))
- return def;
+ /* Verify that the value is of acceptable length */
+ if (!(GET_ATOM_LEN(env, dbgVal, &len) &&
+ (len > 0) &&
+ (len <= sizeof("false"))))
+ return def;
- /* And finally try to extract the value */
- if (!GET_ATOM(env, dbgVal, d, sizeof(d)))
- return def;
+ /* And finally try to extract the value */
+ if (!GET_ATOM(env, dbgVal, d, sizeof(d)))
+ return def;
- if (strncmp(d, "true", len) == 0)
- return TRUE;
- else
- return FALSE;
+ if (strncmp(d, "true", len) == 0)
+ return TRUE;
+ else
+ return FALSE;
- }
+}
static