Skip to content

Commit

Permalink
Add NIFs for system constant lookup
Browse files Browse the repository at this point in the history
Add 2 new NIFs to return the socket level (socket_level/1) and the socket
option name (socket_optname/1) used by getsockopt/4 and setsockopt/4.

Non-portable socket arguments will return {error,unsupported}.

#20

Thanks to @kellymclaughlin and @jaredmorrow!
  • Loading branch information
msantos committed Jun 4, 2015
1 parent 8620dbe commit d522acb
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 443 deletions.
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,22 +229,32 @@ procket works with any version of Erlang after R14A.

See bind(2).

setsockopt(Socket, Level, Optname, Optval) -> ok | {error, posix}
setsockopt(Socket, Level, Optname, Optval) -> ok | Error

Types Socket = integer()
Level = integer() | atom()
Optname = integer() | atom()
Optval = binary()
Error = {error, posix() | unsupported}

See setsockopt(2).

getsockopt(Socket, Level, Optname, Optval) -> {ok, Buf} | {error, posix}
Level and Optname can either be an integer or an atom with the
same name as the definitions in the system header files, e.g.,
'IPPROTO_IPIP', 'SO_REUSEPORT'. Note these are uppercase atoms
and so must be quoted.

If an atom is used as an argument and is not supported by the OS,
setsockopt/4 will return {error,unsupported}.

getsockopt(Socket, Level, Optname, Optval) -> {ok, Buf} | Error

Types Socket = integer()
Level = integer() | atom()
Optname = integer() | atom()
Optval = binary()
Buf = binary()
Error = {error, posix() | unsupported}

See getsockopt(2). Similar to inet:getopts/2 but can be used
with file descriptors.
Expand All @@ -253,6 +263,8 @@ procket works with any version of Erlang after R14A.
binary to indicate no option value is supplied or will be
returned.

Also see setsockopt/4.

ioctl(FD, Request, Arg) -> {ok, Result} | {error, posix()}

Types FD = integer()
Expand Down
148 changes: 45 additions & 103 deletions c_src/procket.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,15 @@
#include "erl_driver.h"
#include "ancillary.h"
#include "procket.h"
#include "sockopt.h"
#include "procket_constants.h"

static ERL_NIF_TERM error_tuple(ErlNifEnv *env, int errnum);
void alloc_free(ErlNifEnv *env, void *obj);

static ERL_NIF_TERM atom_ok;
static ERL_NIF_TERM atom_error;
static ERL_NIF_TERM atom_eagain;
static ERL_NIF_TERM atom_undefined;

static ErlNifResourceType *PROCKET_ALLOC_RESOURCE;

Expand Down Expand Up @@ -84,6 +85,7 @@ load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
atom_ok = enif_make_atom(env, "ok");
atom_error = enif_make_atom(env, "error");
atom_eagain = enif_make_atom(env, "eagain");
atom_undefined = enif_make_atom(env, "undefined");

if ( (PROCKET_ALLOC_RESOURCE = enif_open_resource_type(env, NULL,
"procket_alloc_resource", alloc_free,
Expand Down Expand Up @@ -528,58 +530,10 @@ nif_setsockopt(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
return enif_make_badarg(env);

if (!enif_get_int(env, argv[1], &level))
{
unsigned atom_length;
if (enif_get_atom_length(env, argv[1], &atom_length, ERL_NIF_LATIN1))
{
char *atom_buf = enif_alloc(atom_length);
if (enif_get_atom(env, argv[1], atom_buf, atom_length+1, ERL_NIF_LATIN1))
{
if (!level_lookup(atom_buf, atom_length, &level))
{
enif_free(atom_buf);
return enif_make_badarg(env);
}
enif_free(atom_buf);
}
else
{
enif_free(atom_buf);
return enif_make_badarg(env);
}
}
else
{
return enif_make_badarg(env);
}
}
return enif_make_badarg(env);

if (!enif_get_int(env, argv[2], &optname))
{
unsigned atom_length2;
if (enif_get_atom_length(env, argv[2], &atom_length2, ERL_NIF_LATIN1))
{
char *atom_buf2 = enif_alloc(atom_length2);
if (enif_get_atom(env, argv[2], atom_buf2, atom_length2+1, ERL_NIF_LATIN1))
{
if (!optname_lookup(atom_buf2, atom_length2, &optname))
{
enif_free(atom_buf2);
return enif_make_badarg(env);
}
enif_free(atom_buf2);
}
else
{
enif_free(atom_buf2);
return enif_make_badarg(env);
}
}
else
{
return enif_make_badarg(env);
}
}
return enif_make_badarg(env);

if (!enif_inspect_binary(env, argv[3], &optval))
return enif_make_badarg(env);
Expand All @@ -606,58 +560,10 @@ nif_getsockopt(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
return enif_make_badarg(env);

if (!enif_get_int(env, argv[1], &level))
{
unsigned atom_length;
if (enif_get_atom_length(env, argv[1], &atom_length, ERL_NIF_LATIN1))
{
char *atom_buf = enif_alloc(atom_length);
if (enif_get_atom(env, argv[1], atom_buf, atom_length+1, ERL_NIF_LATIN1))
{
if (!level_lookup(atom_buf, atom_length, &level))
{
enif_free(atom_buf);
return enif_make_badarg(env);
}
enif_free(atom_buf);
}
else
{
enif_free(atom_buf);
return enif_make_badarg(env);
}
}
else
{
return enif_make_badarg(env);
}
}
return enif_make_badarg(env);

if (!enif_get_int(env, argv[2], &optname))
{
unsigned atom_length2;
if (enif_get_atom_length(env, argv[2], &atom_length2, ERL_NIF_LATIN1))
{
char *atom_buf2 = enif_alloc(atom_length2);
if (enif_get_atom(env, argv[2], atom_buf2, atom_length2+1, ERL_NIF_LATIN1))
{
if (!optname_lookup(atom_buf2, atom_length2, &optname))
{
enif_free(atom_buf2);
return enif_make_badarg(env);
}
enif_free(atom_buf2);
}
else
{
enif_free(atom_buf2);
return enif_make_badarg(env);
}
}
else
{
return enif_make_badarg(env);
}
}
return enif_make_badarg(env);

if (!enif_inspect_binary(env, argv[3], &optval))
return enif_make_badarg(env);
Expand Down Expand Up @@ -1074,6 +980,39 @@ nif_memcpy(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
return atom_ok;
}

static ERL_NIF_TERM
nif_socket_level(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
char buf[256] = {0};
const struct procket_define *p = NULL;

if (!enif_get_atom(env, argv[0], buf, sizeof(buf), ERL_NIF_LATIN1))
return enif_make_badarg(env);

for (p = procket_socket_level; p->key != NULL; p++) {
if (!strcmp(buf, p->key))
return enif_make_int(env, p->val);
}

return atom_undefined;
}

static ERL_NIF_TERM
nif_socket_optname(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
char buf[256] = {0};
const struct procket_define *p = NULL;

if (!enif_get_atom(env, argv[0], buf, sizeof(buf), ERL_NIF_LATIN1))
return enif_make_badarg(env);

for (p = procket_socket_optname; p->key != NULL; p++) {
if (!strcmp(buf, p->key))
return enif_make_int(env, p->val);
}

return atom_undefined;
}

/* 0: errno */
static ERL_NIF_TERM
Expand Down Expand Up @@ -1120,7 +1059,7 @@ static ErlNifFunc nif_funcs[] = {
{"bind", 2, nif_bind},
{"connect", 2, nif_connect},
{"getsockname", 2, nif_getsockname},
{"getsockopt", 4, nif_getsockopt},
{"getsockopt_nif", 4, nif_getsockopt},
{"listen", 2, nif_listen},
{"read", 2, nif_read},
{"write_nif", 2, nif_write},
Expand All @@ -1133,12 +1072,15 @@ static ErlNifFunc nif_funcs[] = {

{"recvfrom", 4, nif_recvfrom},
{"sendto", 4, nif_sendto},
{"setsockopt", 4, nif_setsockopt},
{"setsockopt_nif", 4, nif_setsockopt},

{"alloc_nif", 1, nif_alloc},
{"buf", 1, nif_buf},
{"memcpy", 2, nif_memcpy},

{"socket_level", 1, nif_socket_level},
{"socket_optname", 1, nif_socket_optname},

{"errno_id", 1, nif_errno_id}
};

Expand Down
98 changes: 98 additions & 0 deletions c_src/procket_constants.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
struct procket_define {
const char *key;
int val;
};

const struct procket_define procket_socket_level[] = {
{"SOL_SOCKET", SOL_SOCKET},
{"IPPROTO_IP", IPPROTO_IP},
#ifdef IPPROTO_TP
{"IPPROTO_TP", IPPROTO_TP},
#endif
{"IPPROTO_AH", IPPROTO_AH},
{"IPPROTO_TCP", IPPROTO_TCP},
{"IPPROTO_EGP", IPPROTO_EGP},
{"IPPROTO_PUP", IPPROTO_PUP},
{"IPPROTO_UDP", IPPROTO_UDP},
{"IPPROTO_IDP", IPPROTO_IDP},
#ifdef IPPROTO_GRE
{"IPPROTO_GRE", IPPROTO_GRE},
#endif
{"IPPROTO_ESP", IPPROTO_ESP},
#ifdef IPPROTO_MTP
{"IPPROTO_MTP", IPPROTO_MTP},
#endif
{"IPPROTO_PIM", IPPROTO_PIM},
{"IPPROTO_RAW", IPPROTO_RAW},
{"IPPROTO_IPV6", IPPROTO_IPV6},
{"IPPROTO_ICMP", IPPROTO_ICMP},
{"IPPROTO_IGMP", IPPROTO_IGMP},
#ifdef IPPROTO_IPIP
{"IPPROTO_IPIP", IPPROTO_IPIP},
#endif
#ifdef IPPROTO_DCCP
{"IPPROTO_DCCP", IPPROTO_DCCP},
#endif
{"IPPROTO_RSVP", IPPROTO_RSVP},
#ifdef IPPROTO_COMP
{"IPPROTO_COMP", IPPROTO_COMP},
#endif
{"IPPROTO_SCTP", IPPROTO_SCTP},
{"IPPROTO_ENCAP", IPPROTO_ENCAP},
#ifdef IPPROTO_BEETPH
{"IPPROTO_BEETPH", IPPROTO_BEETPH},
#endif
#ifdef IPPROTO_UDPLITE
{"IPPROTO_UDPLITE", IPPROTO_UDPLITE},
#endif
{NULL, -1}
};

const struct procket_define procket_socket_optname[] = {
{"SO_TYPE", SO_TYPE},
{"SO_DEBUG", SO_DEBUG},
{"SO_ERROR", SO_ERROR},
{"SO_SNDBUF", SO_SNDBUF},
{"SO_RCVBUF", SO_RCVBUF},
{"SO_LINGER", SO_LINGER},
{"SO_SNDTIMEO", SO_SNDTIMEO},
#ifdef SO_PASSCRED
{"SO_PASSCRED", SO_PASSCRED},
#endif
#ifdef SO_PEERCRED
{"SO_PEERCRED", SO_PEERCRED},
#endif
#ifdef SO_RCVLOAWAT
{"SO_RCVLOWAT", SO_RCVLOWAT},
#endif
#ifdef SO_SNDLOWAT
{"SO_SNDLOWAT", SO_SNDLOWAT},
#endif
{"SO_RCVTIMEO", SO_RCVTIMEO},
#ifdef SO_NO_CHECK
{"SO_NO_CHECK", SO_NO_CHECK},
#endif
#ifdef SO_PRIORITY
{"SO_PRIORITY", SO_PRIORITY},
#endif
{"SO_REUSEADDR", SO_REUSEADDR},
{"SO_DONTROUTE", SO_DONTROUTE},
{"SO_BROADCAST", SO_BROADCAST},
{"SO_KEEPALIVE", SO_KEEPALIVE},
{"SO_OOBINLINE", SO_OOBINLINE},
#ifdef SO_BSDCOMPAT
{"SO_BSDCOMPAT", SO_BSDCOMPAT},
#endif
#ifdef SO_REUSEPORT
{"SO_REUSEPORT", SO_REUSEPORT},
#endif
#ifdef SO_SNDBUFFORCE
{"SO_SNDBUFFORCE", SO_SNDBUFFORCE},
#endif
#ifdef SO_RCVBUFFORCE
{"SO_RCVBUFFORCE", SO_RCVBUFFORCE},
#endif
{"IPV6_RECVPKTINFO", IPV6_RECVPKTINFO},

{NULL, -1}
};
Loading

0 comments on commit d522acb

Please sign in to comment.