Skip to content

Commit

Permalink
Communicate with Unix sockets
Browse files Browse the repository at this point in the history
Break out socket primitives (socket, listen, accept, ...) to allow Erlang
to create and access Unix sockets. Any type of unprivileged socket can
be created without spawning the setuid helper.

Warning: because listen/2 is now a wrapper around the listen(2), the
procket:listen/1,2 has been renamed to procket:open/1,2.
  • Loading branch information
msantos committed Dec 15, 2010
1 parent bfffa43 commit 560517c
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 57 deletions.
20 changes: 13 additions & 7 deletions README
@@ -1,14 +1,20 @@


procket is an Erlang interface for requesting socket features that procket is an Erlang socket interface which can be used for requesting
usually require superuser privileges. socket features that usually require superuser privileges.


procket uses the experimental NIF interface first introduced in Erlang procket uses the experimental NIF interface first introduced in Erlang
R13B03. R13B03.




CHANGES

* procket:listen/1,2 has been renamed procket:open/1,2. procket:listen/2
is now a wrapper around listen(2)


EXPORTS EXPORTS


listen(Port, Options) -> {ok, FD} | {error, Reason} | {error, {Reason, Description}} open(Port, Options) -> {ok, FD} | {error, Reason} | {error, {Reason, Description}}


Types Port = 0..65535 Types Port = 0..65535
Options = [Opts] Options = [Opts]
Expand Down Expand Up @@ -50,7 +56,7 @@ $ erl -pa ebin
Erlang R13B03 (erts-5.7.4) [source] [rq:1] [async-threads:0] [hipe] [kernel-poll:false] Erlang R13B03 (erts-5.7.4) [source] [rq:1] [async-threads:0] [hipe] [kernel-poll:false]


Eshell V5.7.4 (abort with ^G) Eshell V5.7.4 (abort with ^G)
1> {ok, FD} = procket:listen(53, [{progname, "sudo priv/procket"},{protocol, udp},{type, dgram}]). 1> {ok, FD} = procket:open(53, [{progname, "sudo priv/procket"},{protocol, udp},{type, dgram}]).
{ok,9} {ok,9}
2> {ok, S} = gen_udp:open(53, [{fd,FD}]). 2> {ok, S} = gen_udp:open(53, [{fd,FD}]).
{ok,#Port<0.929>} {ok,#Port<0.929>}
Expand All @@ -76,7 +82,7 @@ ICMP Ping:


Sniff the network: Sniff the network:


1> {ok, S} = procket:listen(0, [{protocol, 16#0008}, {type, raw}, {family, packet}]). 1> {ok, S} = procket:open(0, [{protocol, 16#0008}, {type, raw}, {family, packet}]).
{ok,12} {ok,12}
2> procket:recvfrom(S, 2048). 2> procket:recvfrom(S, 2048).
{ok,<<0,21,175,89,8,38,0,3,82,3,39,36,8,0,69,0,0,52,242, {ok,<<0,21,175,89,8,38,0,3,82,3,39,36,8,0,69,0,0,52,242,
Expand All @@ -85,9 +91,9 @@ Sniff the network:
4> gen_udp:recv(S1, 2048). 4> gen_udp:recv(S1, 2048).


Bind to one or more interfaces: Bind to one or more interfaces:
1> procket:listen(53, [{progname, "sudo priv/procket"},{protocol, udp},{type,dgram},{interface, "br0"}]). 1> procket:open(53, [{progname, "sudo priv/procket"},{protocol, udp},{type,dgram},{interface, "br0"}]).
{ok,9} {ok,9}
2> procket:listen(53, [{progname, "sudo priv/procket"},{protocol, udp},{type,dgram},{interface, "br1"}]). 2> procket:open(53, [{progname, "sudo priv/procket"},{protocol, udp},{type,dgram},{interface, "br1"}]).
{ok,10} {ok,10}


HOW IT WORKS HOW IT WORKS
Expand Down
141 changes: 106 additions & 35 deletions c_src/procket.c
Expand Up @@ -54,62 +54,106 @@ load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
} }




/* Retrieve the file descriptor from the forked privileged process */
/* 0: connected Unix socket */
static ERL_NIF_TERM static ERL_NIF_TERM
nif_open(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) nif_fdrecv(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{ {
int sock_fd = -1; int fd = -1; /* connected socket */
struct sockaddr_un sa = { 0 }; int s = -1; /* socket received from pipe */
int flags = 0;




if (enif_get_string(env, argv[0], sa.sun_path, sizeof(sa.sun_path), ERL_NIF_LATIN1) < 1) if (!enif_get_int(env, argv[0], &fd))
return enif_make_badarg(env); return enif_make_badarg(env);


sa.sun_family = PF_LOCAL; if (ancil_recv_fd(fd, &s) < 0) {

(void)close(fd);
sock_fd = socket(PF_LOCAL, SOCK_STREAM, 0);
if (sock_fd < 0)
return error_tuple(env, errno); return error_tuple(env, errno);
}


flags = fcntl(sock_fd, F_GETFL, 0); (void)close(fd);
flags |= O_NONBLOCK;
(void)fcntl(sock_fd, F_SETFL, flags); return enif_make_tuple(env, 2,
atom_ok,
enif_make_int(env, s));
}


/* 0: domain, 1: type, 2: protocol */
static ERL_NIF_TERM
nif_socket(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
int s = -1;
int domain = 0;
int type = 0;
int protocol = 0;
int flags = 0;


if (bind(sock_fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
return error_tuple(env, errno);


if (listen(sock_fd, BACKLOG) < 0) if (!enif_get_int(env, argv[0], &domain))
return enif_make_badarg(env);

if (!enif_get_int(env, argv[1], &type))
return enif_make_badarg(env);

if (!enif_get_int(env, argv[2], &protocol))
return enif_make_badarg(env);

s = socket(domain, type, protocol);
if (s < 0)
return error_tuple(env, errno); return error_tuple(env, errno);


flags = fcntl(s, F_GETFL, 0);
flags |= O_NONBLOCK;
(void)fcntl(s, F_SETFL, flags);

return enif_make_tuple(env, 2, return enif_make_tuple(env, 2,
atom_ok, atom_ok,
enif_make_int(env, sock_fd)); enif_make_int(env, s));
} }




/* 0: file descriptor, 1: backlog */
static ERL_NIF_TERM static ERL_NIF_TERM
nif_poll(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) nif_listen(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{ {
int sock_fd = -1; /* listening socket */ int s = -1;
int fd = -1; /* connected socket */ int backlog = 5;
int s = -1; /* socket received from pipe */
struct sockaddr_un sa = { 0 };
socklen_t socklen = 0;




if (!enif_get_int(env, argv[0], &sock_fd)) if (!enif_get_int(env, argv[0], &s))
return enif_make_badarg(env); return enif_make_badarg(env);


fd = accept(sock_fd, (struct sockaddr *)&sa, &socklen); if (!enif_get_int(env, argv[1], &backlog))
if (fd < 0) return enif_make_badarg(env);
return error_tuple(env, errno);


if (ancil_recv_fd(fd, &s) < 0) { if (listen(s, backlog) < 0)
(void)close (fd);
return error_tuple(env, errno); return error_tuple(env, errno);
}


(void)close (fd); return atom_ok;
}


/* 0: socket, 1: struct sockaddr */
static ERL_NIF_TERM
nif_accept(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
int l = -1;
int s = -1;
ErlNifBinary sa;
socklen_t salen = 0;


if (!enif_get_int(env, argv[0], &l))
return enif_make_badarg(env);

if (!enif_inspect_binary(env, argv[1], &sa))
return enif_make_badarg(env);

salen = sa.size;
s = accept(l, (sa.size == 0 ? NULL : (struct sockaddr *)sa.data), &salen);
if (s < 0)
return error_tuple(env, errno);


return enif_make_tuple(env, 2, return enif_make_tuple(env, 2,
atom_ok, atom_ok,
Expand Down Expand Up @@ -196,7 +240,6 @@ nif_sendto(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
ErlNifBinary buf; ErlNifBinary buf;
ErlNifBinary sa; ErlNifBinary sa;



if (!enif_get_int(env, argv[0], &sockfd)) if (!enif_get_int(env, argv[0], &sockfd))
return enif_make_badarg(env); return enif_make_badarg(env);


Expand All @@ -209,7 +252,9 @@ nif_sendto(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
if (!enif_inspect_binary(env, argv[3], &sa)) if (!enif_inspect_binary(env, argv[3], &sa))
return enif_make_badarg(env); return enif_make_badarg(env);


if (sendto(sockfd, buf.data, buf.size, flags, (struct sockaddr *)sa.data, sa.size) == -1) if (sendto(sockfd, buf.data, buf.size, flags,
(sa.size == 0 ? NULL : (struct sockaddr *)sa.data),
sa.size) == -1)
return error_tuple(env, errno); return error_tuple(env, errno);


return atom_ok; return atom_ok;
Expand All @@ -230,12 +275,34 @@ nif_bind(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
if (!enif_inspect_binary(env, argv[1], &sa)) if (!enif_inspect_binary(env, argv[1], &sa))
return enif_make_badarg(env); return enif_make_badarg(env);


if (bind(s, (struct sockaddr *)sa.data, sa.size) < 0) if (bind(s, (sa.size == 0 ? NULL : (struct sockaddr *)sa.data), sa.size) < 0)
return error_tuple(env, errno); return error_tuple(env, errno);


return atom_ok; return atom_ok;
} }



/* 0: socket descriptor, 1: struct sockaddr */
static ERL_NIF_TERM
nif_connect(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
int s = -1;
ErlNifBinary sa;


if (!enif_get_int(env, argv[0], &s))
return enif_make_badarg(env);

if (!enif_inspect_binary(env, argv[1], &sa))
return enif_make_badarg(env);

if (connect(s, (sa.size == 0 ? NULL : (struct sockaddr *)sa.data), sa.size) < 0)
return error_tuple(env, errno);

return atom_ok;
}


/* 0: (int)socket descriptor, 1: (int)device dependent request, /* 0: (int)socket descriptor, 1: (int)device dependent request,
* 2: (char *)argp, pointer to structure * 2: (char *)argp, pointer to structure
*/ */
Expand Down Expand Up @@ -308,13 +375,17 @@ error_tuple(ErlNifEnv *env, int errnum)




static ErlNifFunc nif_funcs[] = { static ErlNifFunc nif_funcs[] = {
{"open", 1, nif_open}, {"fdrecv", 1, nif_fdrecv},
{"poll", 1, nif_poll},
{"close", 1, nif_close}, {"close", 1, nif_close},
{"close", 2, nif_close}, {"close", 2, nif_close},
{"accept", 2, nif_accept},
{"bind", 2, nif_bind}, {"bind", 2, nif_bind},
{"connect", 2, nif_connect},
{"listen", 2, nif_listen},
{"recvfrom", 2, nif_recvfrom}, {"recvfrom", 2, nif_recvfrom},
{"ioctl", 3, nif_ioctl}, {"ioctl", 3, nif_ioctl},
{"socket", 3, nif_socket},
{"sendto", 4, nif_sendto}, {"sendto", 4, nif_sendto},
{"setsockopt", 4, nif_setsockopt} {"setsockopt", 4, nif_setsockopt}
}; };
Expand Down
2 changes: 1 addition & 1 deletion ebin/procket.app
@@ -1,7 +1,7 @@
{application, procket, {application, procket,
[ [
{description, "Low level socket operations"}, {description, "Low level socket operations"},
{vsn, "0.01"}, {vsn, "0.02"},
{modules, [ {modules, [
procket, procket,
packet, packet,
Expand Down
2 changes: 1 addition & 1 deletion src/echo.erl
Expand Up @@ -44,7 +44,7 @@ start(udp) ->
start(?PORT, [{protocol, udp}, {family, inet}, {type, dgram}]). start(?PORT, [{protocol, udp}, {family, inet}, {type, dgram}]).
start(Port, Options) -> start(Port, Options) ->
Protocol = proplists:get_value(protocol, Options, tcp), Protocol = proplists:get_value(protocol, Options, tcp),
{ok, Fd} = procket:listen(Port, Options), {ok, Fd} = procket:open(Port, Options),
io:format("Listening on: ~p/~p~n", [Port, Protocol]), io:format("Listening on: ~p/~p~n", [Port, Protocol]),
listen(Protocol, Fd, Port). listen(Protocol, Fd, Port).


Expand Down
2 changes: 1 addition & 1 deletion src/icmp.erl
Expand Up @@ -56,7 +56,7 @@ ping(IP) ->
ping(IP, N) -> ping(IP, N) ->
crypto:start(), crypto:start(),
Id = crypto:rand_uniform(0, 16#FFFF), Id = crypto:rand_uniform(0, 16#FFFF),
{ok, FD} = procket:listen(0, [{protocol, icmp}, {type, raw}, {family, inet}]), {ok, FD} = procket:open(0, [{protocol, icmp}, {type, raw}, {family, inet}]),
{ok, S} = gen_udp:open(0, [binary, {fd, FD}]), {ok, S} = gen_udp:open(0, [binary, {fd, FD}]),
loop(#state{ loop(#state{
s = S, s = S,
Expand Down
2 changes: 1 addition & 1 deletion src/packet.erl
Expand Up @@ -70,7 +70,7 @@ socket() ->
socket(?ETH_P_IP). socket(?ETH_P_IP).
socket(EthType) when is_integer(EthType) -> socket(EthType) when is_integer(EthType) ->
<<Protocol:16>> = <<EthType:16/native>>, <<Protocol:16>> = <<EthType:16/native>>,
procket:listen(0, [{protocol, Protocol}, {type, raw}, {family, packet}]). procket:open(0, [{protocol, Protocol}, {type, raw}, {family, packet}]).




%%------------------------------------------------------------------------- %%-------------------------------------------------------------------------
Expand Down

0 comments on commit 560517c

Please sign in to comment.