Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Communicate with Unix sockets

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...
commit 560517c98771390b4f02fe2207e494497914ffa4 1 parent bfffa43
@msantos authored
View
20 README
@@ -1,14 +1,20 @@
-procket is an Erlang interface for requesting socket features that
-usually require superuser privileges.
+procket is an Erlang socket interface which can be used for requesting
+socket features that usually require superuser privileges.
procket uses the experimental NIF interface first introduced in Erlang
R13B03.
+CHANGES
+
+* procket:listen/1,2 has been renamed procket:open/1,2. procket:listen/2
+ is now a wrapper around listen(2)
+
+
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
Options = [Opts]
@@ -50,7 +56,7 @@ $ erl -pa ebin
Erlang R13B03 (erts-5.7.4) [source] [rq:1] [async-threads:0] [hipe] [kernel-poll:false]
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}
2> {ok, S} = gen_udp:open(53, [{fd,FD}]).
{ok,#Port<0.929>}
@@ -76,7 +82,7 @@ ICMP Ping:
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}
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,
@@ -85,9 +91,9 @@ Sniff the network:
4> gen_udp:recv(S1, 2048).
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}
-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}
HOW IT WORKS
View
141 c_src/procket.c
@@ -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
-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;
- struct sockaddr_un sa = { 0 };
- int flags = 0;
+ int fd = -1; /* connected socket */
+ int s = -1; /* socket received from pipe */
- 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);
- sa.sun_family = PF_LOCAL;
-
- sock_fd = socket(PF_LOCAL, SOCK_STREAM, 0);
- if (sock_fd < 0)
+ if (ancil_recv_fd(fd, &s) < 0) {
+ (void)close(fd);
return error_tuple(env, errno);
+ }
- flags = fcntl(sock_fd, F_GETFL, 0);
- flags |= O_NONBLOCK;
- (void)fcntl(sock_fd, F_SETFL, flags);
+ (void)close(fd);
+
+ 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);
+ flags = fcntl(s, F_GETFL, 0);
+ flags |= O_NONBLOCK;
+ (void)fcntl(s, F_SETFL, flags);
+
return enif_make_tuple(env, 2,
atom_ok,
- enif_make_int(env, sock_fd));
+ enif_make_int(env, s));
}
+/* 0: file descriptor, 1: backlog */
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 fd = -1; /* connected socket */
- int s = -1; /* socket received from pipe */
- struct sockaddr_un sa = { 0 };
- socklen_t socklen = 0;
+ int s = -1;
+ int backlog = 5;
- if (!enif_get_int(env, argv[0], &sock_fd))
+ if (!enif_get_int(env, argv[0], &s))
return enif_make_badarg(env);
- fd = accept(sock_fd, (struct sockaddr *)&sa, &socklen);
- if (fd < 0)
- return error_tuple(env, errno);
+ if (!enif_get_int(env, argv[1], &backlog))
+ return enif_make_badarg(env);
- if (ancil_recv_fd(fd, &s) < 0) {
- (void)close (fd);
+ if (listen(s, backlog) < 0)
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,
atom_ok,
@@ -196,7 +240,6 @@ nif_sendto(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
ErlNifBinary buf;
ErlNifBinary sa;
-
if (!enif_get_int(env, argv[0], &sockfd))
return enif_make_badarg(env);
@@ -209,7 +252,9 @@ nif_sendto(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
if (!enif_inspect_binary(env, argv[3], &sa))
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 atom_ok;
@@ -230,12 +275,34 @@ nif_bind(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
if (!enif_inspect_binary(env, argv[1], &sa))
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 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,
* 2: (char *)argp, pointer to structure
*/
@@ -308,13 +375,17 @@ error_tuple(ErlNifEnv *env, int errnum)
static ErlNifFunc nif_funcs[] = {
- {"open", 1, nif_open},
- {"poll", 1, nif_poll},
+ {"fdrecv", 1, nif_fdrecv},
+
{"close", 1, nif_close},
{"close", 2, nif_close},
+ {"accept", 2, nif_accept},
{"bind", 2, nif_bind},
+ {"connect", 2, nif_connect},
+ {"listen", 2, nif_listen},
{"recvfrom", 2, nif_recvfrom},
{"ioctl", 3, nif_ioctl},
+ {"socket", 3, nif_socket},
{"sendto", 4, nif_sendto},
{"setsockopt", 4, nif_setsockopt}
};
View
2  ebin/procket.app
@@ -1,7 +1,7 @@
{application, procket,
[
{description, "Low level socket operations"},
- {vsn, "0.01"},
+ {vsn, "0.02"},
{modules, [
procket,
packet,
View
2  src/echo.erl
@@ -44,7 +44,7 @@ start(udp) ->
start(?PORT, [{protocol, udp}, {family, inet}, {type, dgram}]).
start(Port, Options) ->
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]),
listen(Protocol, Fd, Port).
View
2  src/icmp.erl
@@ -56,7 +56,7 @@ ping(IP) ->
ping(IP, N) ->
crypto:start(),
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}]),
loop(#state{
s = S,
View
2  src/packet.erl
@@ -70,7 +70,7 @@ socket() ->
socket(?ETH_P_IP).
socket(EthType) when is_integer(EthType) ->
<<Protocol:16>> = <<EthType:16/native>>,
- procket:listen(0, [{protocol, Protocol}, {type, raw}, {family, packet}]).
+ procket:open(0, [{protocol, Protocol}, {type, raw}, {family, packet}]).
%%-------------------------------------------------------------------------
View
52 src/procket.erl
@@ -29,9 +29,13 @@
%% ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
%% POSSIBILITY OF SUCH DAMAGE.
-module(procket).
+-include("procket.hrl").
-export([
- init/0,open/1,poll/1,close/1,close/2,listen/1,listen/2,
+ init/0,open/1,open/2,
+ socket/3, listen/2,connect/2,
+ accept/1,accept/2,
+ fdopen/1,fdrecv/1,close/1,close/2,
recvfrom/2,sendto/4,bind/2,
ioctl/3,setsockopt/4
]).
@@ -46,24 +50,36 @@ init() ->
on_load() ->
erlang:load_nif(progname(), []).
-open(_) ->
- erlang:error(not_implemented).
close(_) ->
erlang:error(not_implemented).
-poll(_) ->
+fdrecv(_) ->
erlang:error(not_implemented).
close(_,_) ->
erlang:error(not_implemented).
+accept(Socket) ->
+ accept(Socket, <<>>).
+accept(_,_) ->
+ erlang:error(not_implemented).
+
bind(_,_) ->
erlang:error(not_implemented).
+connect(_,_) ->
+ erlang:error(not_implemented).
+
+listen(_,_) ->
+ erlang:error(not_implemented).
+
recvfrom(_,_) ->
erlang:error(not_implemented).
+socket(_,_,_) ->
+ erlang:error(not_implemented).
+
ioctl(_,_,_) ->
erlang:error(not_implemented).
@@ -73,9 +89,10 @@ sendto(_,_,_,_) ->
setsockopt(_,_,_,_) ->
erlang:error(not_implemented).
-listen(Port) ->
- listen(Port, []).
-listen(Port, Options) when is_integer(Port), is_list(Options) ->
+
+open(Port) ->
+ open(Port, []).
+open(Port, Options) when is_integer(Port), is_list(Options) ->
Opt = case proplists:get_value(pipe, Options) of
undefined ->
Tmp = mktmp:dirname(),
@@ -85,15 +102,15 @@ listen(Port, Options) when is_integer(Port), is_list(Options) ->
_ ->
[{tmpdir, false}] ++ Options
end,
- listen1(Port, Opt).
+ open1(Port, Opt).
-listen1(Port, Options) ->
+open1(Port, Options) ->
Pipe = proplists:get_value(pipe, Options),
- {ok, Sockfd} = open(Pipe),
+ {ok, Sockfd} = fdopen(Pipe),
Cmd = make_args(Port, Options),
case os:cmd(Cmd) of
[] ->
- FD = poll(Sockfd),
+ FD = fdget(Sockfd),
cleanup(Sockfd, Pipe, Options),
FD;
Error ->
@@ -110,6 +127,19 @@ cleanup(Sockfd, Pipe, Options) ->
mktmp:close(Path)
end.
+fdopen(Path) when is_list(Path) ->
+ fdopen(list_to_binary(Path));
+fdopen(Path) when is_binary(Path), byte_size(Path) < ?UNIX_PATH_MAX ->
+ {ok, Socket} = socket(?PF_LOCAL, ?SOCK_STREAM, 0),
+ Sun = <<?PF_LOCAL:16/native, Path/binary, 0:((?UNIX_PATH_MAX-byte_size(Path))*8)>>,
+ ok = bind(Socket, Sun),
+ ok = listen(Socket, ?BACKLOG),
+ {ok, Socket}.
+
+fdget(Socket) ->
+ {ok, S} = accept(Socket),
+ fdrecv(S).
+
make_args(Port, Options) ->
Bind = " " ++ case proplists:lookup(ip, Options) of
none ->
Please sign in to comment.
Something went wrong with that request. Please try again.