Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 7b9cb52a2f

Fetching latest commit…

Cannot retrieve the latest commit at this time

README
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

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

* procket:recvfrom/2 returns {error,eagain} when data is not available
  (previously return nodata)


EXPORTS

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

    Types   Port = 0..65535
            Options = [Opts]
            Opts = {pipe, Path} | {protocol, Protocol} | {ip, IPAddress} |
                    {progname, string()} | {interface, string()}
            Protocol = tcp | udp
            IPAddress = string() | tuple()
            Reason = posix()
            Description = string()


COMPILING

Try running: make


SETUID vs SUDO

The procket executable needs root privileges. Either allow your user to
run procket using sudo or copy procket to somewhere owned by root and
make it setuid.

* for sudo

    sudo visudo
    youruser ALL=NOPASSWD: /path/to/procket/priv/procket

* to make it setuid

    sudo cp priv/procket /usr/local/bin
    sudo chown root:yourgroup /usr/local/bin/procket
    sudo chmod 750 /usr/local/bin/procket
    sudo chmod u+s /usr/local/bin/procket


USING IT

$ 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: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>}
3> receive M -> M end.
{udp,#Port<0.929>,{127,0,0,1},47483,"hello\n"}
4> 

$ nc -u localhost 53
hello
^C


EXAMPLE

echo is a sample client using procket.

$ erl -pa ebin
1> echo:start(53, [{progname, "sudo priv/procket"}, {protocol, tcp}]).

ICMP Ping:

1> icmp:ping("www.yahoo.com").

Sniff the network:

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,
          0,0,0,52,6,188,81,209,...>>}
3> {ok, S1} = gen_udp:open(0, [binary, {fd, S}, {active, false}]).
4> gen_udp:recv(S1, 2048).

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

HOW IT WORKS

procket creates a local domain socket and spawns a small setuid binary
(or runs it under sudo). The executable opens a socket, drops privs and
passes the file descriptor back to Erlang over the Unix socket.

procket uses libancillary for passing file descriptors between processes:

    http://www.normalesup.org/~george/comp/libancillary/


TODO

* Support BPF on BSD for sending/receiving packets

* Examples/changes to support sending TCP/UDP packets using the raw
  socket interface, using epcap to receive packets

* Docs and type specs

* Try to re-use the Unix socket when requesting more fd's from the procket
  executable

* Make a procket gen_server (gen_raw(?)). Support passive and active
  modes, using recvfrom/2. Experiment with erlang:open_port/2 and the
  fd option for active mode.

  Hold state for the socket, so the caller does not need to, e.g.,
  use ifindex/2.


CONTRIBUTORS

Magnus Klaar:
* support for binding a socket to an interface
* Makefile fixes

Gregory Haskins:
* fix link-error on SUSE platforms

Something went wrong with that request. Please try again.