Skip to content
Browse files

Interface for requesting privileged sockets in Erlang.

  • Loading branch information...
0 parents commit 441fde8410be77fbe09bcf53be7f888f76a9ff06 @msantos committed Jan 9, 2010
Showing with 1,334 additions and 0 deletions.
  1. +2 −0 .gitignore
  2. +6 −0 Emakefile
  3. +36 −0 Makefile
  4. +88 −0 README
  5. +71 −0 c_src/Makefile.ancillary
  6. +123 −0 c_src/ancillary.h
  7. +98 −0 c_src/fd_recv.c
  8. +92 −0 c_src/fd_send.c
  9. +239 −0 c_src/procket.c
  10. +101 −0 c_src/procket.h
  11. +235 −0 c_src/procket_cmd.c
  12. +74 −0 src/echo.erl
  13. +75 −0 src/mktmp.erl
  14. +94 −0 src/procket.erl
2 .gitignore
@@ -0,0 +1,2 @@
+*.[oa]
+*.beam
6 Emakefile
@@ -0,0 +1,6 @@
+{["src/*"],
+ [{i, "include"},
+ {outdir, "ebin"},
+ debug_info]
+}.
+
36 Makefile
@@ -0,0 +1,36 @@
+
+ERL=erl
+APP=procket
+
+CC=gcc
+
+#ERL_LIB=/usr/local/lib/erlang
+ERL_ROOT=/media/opt/local/lib/erlang
+ARCH=-m32
+CFLAGS=-g -Wall
+FLAGS=-fPIC -shared
+
+
+all: dir erl ancillary nif cmd
+
+dir:
+ -@mkdir -p priv/tmp ebin
+
+erl:
+ @$(ERL) -noinput +B \
+ -eval 'case make:all() of up_to_date -> halt(0); error -> halt(1) end.'
+
+ancillary:
+ (cd c_src && $(MAKE) -f Makefile.ancillary)
+
+nif:
+ $(CC) -g -Wall $(FLAGS) -o priv/procket.so -L c_src \
+ c_src/procket.c -lancillary -I $(ERL_ROOT)/usr/include/
+
+cmd:
+ $(CC) -g -Wall -o priv/procket -L c_src c_src/procket_cmd.c -lancillary
+
+clean:
+ @rm -fv ebin/*.beam priv/$(APP) priv/$(APP).so c_src/*.a c_src/*.o
+
+
88 README
@@ -0,0 +1,88 @@
+
+procket is an Erlang interface for requesting socket features that
+usually require superuser privileges.
+
+procket uses the experimental NIF interface first introduced in Erlang
+R13B03.
+
+
+EXPORTS
+
+listen(Port, Options) -> {ok, FD} | {error, Reason} | {error, Reason, Description}
+
+ Types Port = 0..65535
+ Options = [Opts]
+ Opts = {pipe, Path} | {protocol, Protocol} | {ip, IPAddress} |
+ {progname, string()}
+ Protocol = tcp | udp
+ IPAddress = string() | tuple()
+
+
+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:listen(53, [{progname, "sudo priv/procket"},{protocol, udp}]).
+{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}]).
+
+
+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 libanciallary for passing file descriptors between processes:
+
+ http://www.normalesup.org/~george/comp/libancillary/
+
+
+TODO
+
+* add an interface for setsockopt(2)
+
+* does Erlang close the passed in fd? add an interface to close() it
+
+* allow procket to manage more than one fd
+
+* experiment with providing an interface for supporting for raw sockets
+
+
71 c_src/Makefile.ancillary
@@ -0,0 +1,71 @@
+###########################################################################
+# libancillary - black magic on Unix domain sockets
+# (C) Nicolas George
+# Makefile - guess what
+###########################################################################
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. The name of the author may not be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+CC=gcc
+CFLAGS=-Wall -g -O2
+LDFLAGS=
+LIBS=
+AR=ar
+RANLIB=ranlib
+RM=rm
+CP=cp
+MKDIR=mkdir
+TAR=tar
+GZIP=gzip -9
+
+NAME=libancillary
+DISTRIBUTION=API COPYING Makefile ancillary.h fd_send.c fd_recv.c test.c
+VERSION=0.9.1
+
+OBJECTS=fd_send.o fd_recv.o
+
+TUNE_OPTS=-DNDEBUG -DSPARE_SEND_FDS -DSPARE_RECV_FDS
+
+.c.o:
+ $(CC) -c $(CFLAGS) $(TUNE_OPTS) $<
+
+all: libancillary.a
+
+libancillary.a: $(OBJECTS)
+ $(AR) cr $@ $(OBJECTS)
+ $(RANLIB) $@
+
+fd_send.o: ancillary.h
+fd_recv.o: ancillary.h
+
+test: test.c libancillary.a
+ $(CC) -o $@ $(CFLAGS) $(LDFLAGS) -L. test.c -lancillary $(LIBS)
+
+clean:
+ -$(RM) -f *.o *.a test
+
+dist:
+ $(MKDIR) $(NAME)-$(VERSION)
+ $(CP) $(DISTRIBUTION) $(NAME)-$(VERSION)
+ $(TAR) -cf - $(NAME)-$(VERSION) | $(GZIP) > $(NAME)-$(VERSION).tar.gz
+ $(RM) -rf $(NAME)-$(VERSION)
123 c_src/ancillary.h
@@ -0,0 +1,123 @@
+/***************************************************************************
+ * libancillary - black magic on Unix domain sockets
+ * (C) Nicolas George
+ * ancillary.h - public header
+ ***************************************************************************/
+
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ANCILLARY_H__
+#define ANCILLARY_H__
+
+/***************************************************************************
+ * Start of the readable part.
+ ***************************************************************************/
+
+#define ANCIL_MAX_N_FDS 960
+/*
+ * Maximum number of fds that can be sent or received using the "esay"
+ * functions; this is so that all can fit in one page.
+ */
+
+extern int
+ancil_send_fds_with_buffer(int, const int *, unsigned, void *);
+/*
+ * ancil_send_fds_with_buffer(sock, n_fds, fds, buffer)
+ *
+ * Sends the file descriptors in the array pointed by fds, of length n_fds
+ * on the socket sock.
+ * buffer is a writeable memory area large enough to hold the required data
+ * structures.
+ * Returns: -1 and errno in case of error, 0 in case of success.
+ */
+
+extern int
+ancil_recv_fds_with_buffer(int, int *, unsigned, void *);
+/*
+ * ancil_recv_fds_with_buffer(sock, n_fds, fds, buffer)
+ *
+ * Receives *n_fds file descriptors into the array pointed by fds
+ * from the socket sock.
+ * buffer is a writeable memory area large enough to hold the required data
+ * structures.
+ * Returns: -1 and errno in case of error, the actual number of received fd
+ * in case of success
+ */
+
+#define ANCIL_FD_BUFFER(n) \
+ struct { \
+ struct cmsghdr h; \
+ int fd[n]; \
+ }
+/* ANCIL_FD_BUFFER(n)
+ *
+ * A structure type suitable to be used as buffer for n file descriptors.
+ * Requires <sys/socket.h>.
+ * Example:
+ * ANCIL_FD_BUFFER(42) buffer;
+ * ancil_recv_fds_with_buffer(sock, 42, my_fds, &buffer);
+ */
+
+extern int
+ancil_send_fds(int, const int *, unsigned);
+/*
+ * ancil_send_fds(sock, n_fds, fds)
+ *
+ * Sends the file descriptors in the array pointed by fds, of length n_fds
+ * on the socket sock.
+ * n_fds must not be greater than ANCIL_MAX_N_FDS.
+ * Returns: -1 and errno in case of error, 0 in case of success.
+ */
+
+extern int
+ancil_recv_fds(int, int *, unsigned);
+/*
+ * ancil_recv_fds(sock, n_fds, fds)
+ *
+ * Receives *n_fds file descriptors into the array pointed by fds
+ * from the socket sock.
+ * *n_fds must not be greater than ANCIL_MAX_N_FDS.
+ * Returns: -1 and errno in case of error, the actual number of received fd
+ * in case of success.
+ */
+
+
+extern int
+ancil_send_fd(int, int);
+/* ancil_recv_fd(sock, fd);
+ *
+ * Sends the file descriptor fd on the socket sock.
+ * Returns : -1 and errno in case of error, 0 in case of success.
+ */
+
+extern int
+ancil_recv_fd(int, int *);
+/* ancil_send_fd(sock, &fd);
+ *
+ * Receives the file descriptor fd from the socket sock.
+ * Returns : -1 and errno in case of error, 0 in case of success.
+ */
+
+#endif /* ANCILLARY_H__ */
98 c_src/fd_recv.c
@@ -0,0 +1,98 @@
+/***************************************************************************
+ * libancillary - black magic on Unix domain sockets
+ * (C) Nicolas George
+ * fd_send.c - receiving file descriptors
+ ***************************************************************************/
+
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _XPG4_2 /* Solaris sucks */
+# define _XPG4_2
+#endif
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <assert.h>
+#if defined(__FreeBSD__)
+# include <sys/param.h> /* FreeBSD sucks */
+#endif
+
+#include "ancillary.h"
+
+int
+ancil_recv_fds_with_buffer(int sock, int *fds, unsigned n_fds, void *buffer)
+{
+ struct msghdr msghdr;
+ char nothing;
+ struct iovec nothing_ptr;
+ struct cmsghdr *cmsg;
+ int i;
+
+ nothing_ptr.iov_base = &nothing;
+ nothing_ptr.iov_len = 1;
+ msghdr.msg_name = NULL;
+ msghdr.msg_namelen = 0;
+ msghdr.msg_iov = &nothing_ptr;
+ msghdr.msg_iovlen = 1;
+ msghdr.msg_flags = 0;
+ msghdr.msg_control = buffer;
+ msghdr.msg_controllen = sizeof(struct cmsghdr) + sizeof(int) * n_fds;
+ cmsg = CMSG_FIRSTHDR(&msghdr);
+ cmsg->cmsg_len = msghdr.msg_controllen;
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ for(i = 0; i < n_fds; i++)
+ ((int *)CMSG_DATA(cmsg))[i] = -1;
+
+ if(recvmsg(sock, &msghdr, 0) < 0)
+ return(-1);
+ for(i = 0; i < n_fds; i++)
+ fds[i] = ((int *)CMSG_DATA(cmsg))[i];
+ n_fds = (cmsg->cmsg_len - sizeof(struct cmsghdr)) / sizeof(int);
+ return(n_fds);
+}
+
+#ifndef SPARE_RECV_FDS
+int
+ancil_recv_fds(int sock, int *fd, unsigned n_fds)
+{
+ ANCIL_FD_BUFFER(ANCIL_MAX_N_FDS) buffer;
+
+ assert(n_fds <= ANCIL_MAX_N_FDS);
+ return(ancil_recv_fds_with_buffer(sock, fd, n_fds, &buffer));
+}
+#endif /* SPARE_RECV_FDS */
+
+#ifndef SPARE_RECV_FD
+int
+ancil_recv_fd(int sock, int *fd)
+{
+ ANCIL_FD_BUFFER(1) buffer;
+
+ return(ancil_recv_fds_with_buffer(sock, fd, 1, &buffer) == 1 ? 0 : -1);
+}
+#endif /* SPARE_RECV_FD */
92 c_src/fd_send.c
@@ -0,0 +1,92 @@
+/***************************************************************************
+ * libancillary - black magic on Unix domain sockets
+ * (C) Nicolas George
+ * fd_send.c - sending file descriptors
+ ***************************************************************************/
+
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _XPG4_2 /* Solaris sucks */
+# define _XPG4_2
+#endif
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <assert.h>
+#if defined(__FreeBSD__)
+# include <sys/param.h> /* FreeBSD sucks */
+#endif
+
+#include "ancillary.h"
+
+int
+ancil_send_fds_with_buffer(int sock, const int *fds, unsigned n_fds, void *buffer)
+{
+ struct msghdr msghdr;
+ char nothing = '!';
+ struct iovec nothing_ptr;
+ struct cmsghdr *cmsg;
+ int i;
+
+ nothing_ptr.iov_base = &nothing;
+ nothing_ptr.iov_len = 1;
+ msghdr.msg_name = NULL;
+ msghdr.msg_namelen = 0;
+ msghdr.msg_iov = &nothing_ptr;
+ msghdr.msg_iovlen = 1;
+ msghdr.msg_flags = 0;
+ msghdr.msg_control = buffer;
+ msghdr.msg_controllen = sizeof(struct cmsghdr) + sizeof(int) * n_fds;
+ cmsg = CMSG_FIRSTHDR(&msghdr);
+ cmsg->cmsg_len = msghdr.msg_controllen;
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ for(i = 0; i < n_fds; i++)
+ ((int *)CMSG_DATA(cmsg))[i] = fds[i];
+ return(sendmsg(sock, &msghdr, 0) >= 0 ? 0 : -1);
+}
+
+#ifndef SPARE_SEND_FDS
+int
+ancil_send_fds(int sock, const int *fds, unsigned n_fds)
+{
+ ANCIL_FD_BUFFER(ANCIL_MAX_N_FDS) buffer;
+
+ assert(n_fds <= ANCIL_MAX_N_FDS);
+ return(ancil_send_fds_with_buffer(sock, fds, n_fds, &buffer));
+}
+#endif /* SPARE_SEND_FDS */
+
+#ifndef SPARE_SEND_FD
+int
+ancil_send_fd(int sock, int fd)
+{
+ ANCIL_FD_BUFFER(1) buffer;
+
+ return(ancil_send_fds_with_buffer(sock, &fd, 1, &buffer));
+}
+#endif /* SPARE_SEND_FD */
239 c_src/procket.c
@@ -0,0 +1,239 @@
+/* Copyright (c) 2010, Michael Santos <michael.santos@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "erl_nif.h"
+#include "ancillary.h"
+#include "procket.h"
+
+typedef struct {
+ int fd;
+ char *path;
+} PRIVDATA;
+
+static ERL_NIF_TERM error_tuple(ErlNifEnv *env, char *atom, char *err);
+static int my_enif_get_string(ErlNifEnv *env, ERL_NIF_TERM list, char *buf, size_t buflen);
+static ERL_NIF_TERM error_message(ErlNifEnv *env, char *atom, char *err, char *msg);
+static ERL_NIF_TERM sock_close(ErlNifEnv *env);
+
+
+ static int
+load(ErlNifEnv *env, void **priv, ERL_NIF_TERM load_info)
+{
+ PRIVDATA *data = NULL;
+
+
+ data = (PRIVDATA *)enif_alloc(env, sizeof(PRIVDATA));
+ if (data == NULL)
+ return (-1);
+
+ *priv = data;
+
+ return (0);
+}
+
+
+ static int
+reload(ErlNifEnv *env, void **priv, ERL_NIF_TERM load_info)
+{
+ (void)sock_close(env);
+ enif_free(env, ((PRIVDATA *)*priv)->path);
+ enif_free(env, *priv);
+
+ return load(env, priv, load_info);
+}
+
+
+ static ERL_NIF_TERM
+sock_open(ErlNifEnv *env, ERL_NIF_TERM path, ERL_NIF_TERM protocol)
+{
+ PRIVDATA *data = NULL;
+ int proto = 0;
+ struct sockaddr_un sa = { 0 };
+ int flags = 0;
+
+
+ data = (PRIVDATA *)enif_get_data(env);
+
+ if (data->path != NULL)
+ return error_tuple(env, "error", "socket_already_open");
+
+ data->path = (char *)enif_alloc(env, UNIX_PATH_MAX);
+ if (data->path == NULL)
+ return error_tuple(env, "error", "memory_allocation_failure");
+
+ if (!my_enif_get_string(env, path, data->path, UNIX_PATH_MAX))
+ return enif_make_badarg(env);
+
+ if (strlen(data->path) == 0)
+ return enif_make_badarg(env);
+
+ if (!enif_get_int(env, protocol, &proto))
+ return enif_make_badarg(env);
+
+ sa.sun_family = PF_LOCAL;
+ (void)memcpy(sa.sun_path, data->path, sizeof(sa.sun_path)-1);
+
+ errno = 0;
+ data->fd = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (data->fd < 0)
+ return error_message(env, "error", "socket", strerror(errno));
+
+ flags = fcntl(data->fd, F_GETFL, 0);
+ flags |= O_NONBLOCK;
+ (void)fcntl(data->fd, F_SETFL, flags);
+
+ errno = 0;
+ if (bind(data->fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
+ return error_message(env, "error", "bind", strerror(errno));
+
+ errno = 0;
+ if (listen(data->fd, 5) < 0)
+ return error_message(env, "error", "listen", strerror(errno));
+
+ return enif_make_atom(env, "ok");
+}
+
+
+ static ERL_NIF_TERM
+poll(ErlNifEnv *env)
+{
+ PRIVDATA *data = NULL;
+ int s= 0;
+ int pipefd = 0;
+ struct sockaddr_un sa = { 0 };
+ socklen_t socklen = 0;
+
+
+ data = (PRIVDATA *)enif_get_data(env);
+
+ if (data->path == NULL)
+ return error_tuple(env, "error", "no_socket");
+
+ errno = 0;
+ pipefd = accept(data->fd, (struct sockaddr *)&sa, &socklen);
+ if (pipefd < 0)
+ return error_message(env, "error", "accept", strerror(errno));
+
+ errno = 0;
+ if (ancil_recv_fd(pipefd, &s) < 0) {
+ (void)close (pipefd);
+ return error_message(env, "error", "recvmsg", strerror(errno));
+ }
+
+ (void)close (pipefd);
+ (void)sock_close(env);
+
+ return enif_make_tuple(env, 2,
+ enif_make_atom(env, "ok"),
+ enif_make_int(env, s));
+}
+
+
+ static ERL_NIF_TERM
+sock_close(ErlNifEnv *env)
+{
+ PRIVDATA *data = NULL;
+
+
+ data = (PRIVDATA *)enif_get_data(env);
+
+ if (data->path == NULL)
+ return error_tuple(env, "error", "no_socket");
+
+ (void)close(data->fd);
+
+ errno = 0;
+ if (unlink(data->path) < 0)
+ return error_message(env, "error", "unlink", strerror(errno));
+
+ if (data->path) {
+ enif_free(env, data->path);
+ data->path = NULL;
+ }
+
+ data->fd = -1;
+
+ return enif_make_atom(env, "ok");
+}
+
+
+ static ERL_NIF_TERM
+error_tuple(ErlNifEnv *env, char *atom, char *err)
+{
+ return enif_make_tuple(env, 2,
+ enif_make_atom(env, atom),
+ enif_make_atom(env, err));
+}
+
+
+ static ERL_NIF_TERM
+error_message(ErlNifEnv *env, char *atom, char *err, char *msg)
+{
+ return enif_make_tuple(env, 3,
+ enif_make_atom(env, atom),
+ enif_make_atom(env, err),
+ enif_make_string(env, msg));
+}
+
+
+/* from:
+ * http://d.hatena.ne.jp/vostok92/20091201/1259680319
+ */
+ static int
+my_enif_get_string(ErlNifEnv *env, ERL_NIF_TERM list, char *buf, size_t buflen)
+{
+ ERL_NIF_TERM head, tail;
+ int val;
+ int n = 1;
+
+
+ while ((n++ < buflen) && (enif_get_list_cell(env, list, &head, &tail))) {
+ if (!enif_get_int(env, head, &val))
+ return 0;
+
+ *buf = (char)val;
+ buf++;
+ list = tail;
+ }
+ *buf = '\0';
+
+ return 1;
+}
+
+static ErlNifFunc nif_funcs[] = {
+ {"open", 2, sock_open},
+ {"poll", 0, poll},
+ {"close", 0, sock_close}
+};
+
+ERL_NIF_INIT(procket, nif_funcs, load, reload, NULL, NULL)
+
+
101 c_src/procket.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2010, Michael Santos <michael.santos@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <err.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include <netinet/in.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <sys/errno.h>
+
+
+#define PROCKET_VERSION "0.01"
+
+#ifndef UNIX_PATH_MAX
+#define UNIX_PATH_MAX sizeof(((struct sockaddr_un *)0)->sun_path)
+#endif
+
+#define MAXBUFLEN 4096 /* Largest message accepted on stdin */
+
+#define IS_ERR(x) do { \
+ if ((x) == -1) { \
+ err(EXIT_FAILURE, "%s", #x); \
+ } \
+} while (0)
+
+#define IS_NULL(x) do { \
+ if ((x) == NULL) \
+ errx(EXIT_FAILURE, "%s", #x); \
+} while (0);
+
+#define IS_FALSE(x) do { \
+ if ((x) != 0) \
+ errx(EXIT_FAILURE, "%s", #x); \
+} while (0);
+
+#define IS_LTZERO(x) do { \
+ if ((x) < 0) \
+ errx(EXIT_FAILURE, "%s", #x); \
+} while (0);
+
+#define VERBOSE(x, ...) do { \
+ if (ep->verbose >= x) { \
+ (void)fprintf (stderr, __VA_ARGS__); \
+ } \
+} while (0)
+
+
+extern char *__progname;
+
+
+typedef struct {
+ char *path; /* path to pipe file */
+ char *bind; /* <port> or <ipaddr:port> */
+ int s; /* socket fd */
+ in_addr_t ipaddr; /* IP Address */
+ in_port_t port; /* Port */
+ int verbose; /* Debug messages */
+
+ int (*open)(void *state);
+} PROCKET_STATE;
+
+
235 c_src/procket_cmd.c
@@ -0,0 +1,235 @@
+/* Copyright (c) 2010, Michael Santos <michael.santos@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "procket.h"
+#include "ancillary.h"
+
+
+#define BACKLOG 50 /* default backlog for TCP connections */
+
+int procket_pipe(PROCKET_STATE *ps);
+
+int procket_open_tcp(void *state);
+int procket_open_udp(void *state);
+int procket_open_icmp(void *state);
+int procket_open_raw(void *state);
+
+int procket_open_socket(void *state, int domain, int type, int protocol);
+void usage(PROCKET_STATE *ep);
+
+
+ int
+main(int argc, char *argv[])
+{
+ PROCKET_STATE *ps = NULL;
+ int ch = 0;
+ char *p = NULL;
+
+
+ IS_NULL(ps = (PROCKET_STATE *)calloc(1, sizeof(PROCKET_STATE)));
+
+ ps->open = &procket_open_tcp;
+ ps->ipaddr = INADDR_ANY;
+
+ while ( (ch = getopt(argc, argv, "p:P:v")) != -1) {
+ switch (ch) {
+ case 'p': /* path to pipe */
+ IS_NULL(ps->path = strdup(optarg));
+ if (strlen(ps->path) >= UNIX_PATH_MAX)
+ usage(ps);
+ break;
+ case 'P': /* type of socket */
+ switch (atoi(optarg)) {
+ case IPPROTO_ICMP:
+ ps->open = &procket_open_icmp;
+ break;
+ case IPPROTO_IP: /* raw socket */
+ ps->open = &procket_open_raw;
+ break;
+ case IPPROTO_TCP:
+ ps->open = &procket_open_tcp;
+ break;
+ case IPPROTO_UDP:
+ ps->open = &procket_open_udp;
+ break;
+ default:
+ usage(ps);
+ break;
+ }
+ break;
+ case 'v':
+ ps->verbose++;
+ break;
+ case 'h':
+ default:
+ usage(ps);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if ( (argc == 0) || (ps->path == NULL))
+ usage(ps);
+
+ IS_NULL(ps->bind = strdup(argv[0]));
+
+ if ( (p = strchr(ps->bind, ':')) == NULL) {
+ ps->port = (in_port_t)atoi(ps->bind);
+ }
+ else {
+ struct in_addr in;
+
+ *p++ = '\0';
+ ps->port = (in_port_t)atoi(p);
+ IS_LTZERO(inet_aton(ps->bind, &in));
+ ps->ipaddr = in.s_addr;
+ }
+
+ IS_LTZERO(ps->open(ps));
+
+ if (setgid(getgid()) == -1)
+ err(EXIT_FAILURE, "setgid");
+ if (setuid(getuid()) == -1)
+ err(EXIT_FAILURE, "setuid");
+
+ IS_LTZERO(procket_pipe(ps));
+
+ exit (0);
+}
+
+
+ int
+procket_open_tcp(void *state)
+{
+ PROCKET_STATE *ps = (PROCKET_STATE *)state;
+ int r = 0;
+
+
+ if ( (r = procket_open_socket(state, PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
+ return (-1);
+
+ if (listen(ps->s, BACKLOG) < 0)
+ err(EXIT_FAILURE, "listen");
+
+ return (0);
+}
+
+
+ int
+procket_open_udp(void *state)
+{
+ return procket_open_socket(state, PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+}
+
+
+ int
+procket_open_icmp(void *state)
+{
+ return procket_open_socket(state, PF_INET, SOCK_RAW, IPPROTO_ICMP);
+}
+
+
+ int
+procket_open_raw(void *state)
+{
+ PROCKET_STATE *ps = (PROCKET_STATE *)state;
+ const int on = 1;
+
+ if (procket_open_socket(state, PF_INET, SOCK_RAW, IPPROTO_TCP) < 0)
+ return (-1);
+
+ if (setsockopt(ps->s, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0)
+ return (-1);
+
+ return (0);
+}
+
+
+ int
+procket_open_socket(void *state, int domain, int type, int protocol)
+{
+ PROCKET_STATE *ps = (PROCKET_STATE *)state;
+ struct sockaddr_in sa = { 0 };
+
+
+ sa.sin_family = domain;
+ sa.sin_port = htons(ps->port);
+ sa.sin_addr.s_addr = ps->ipaddr;
+
+ if ( (ps->s = socket(domain, type, protocol)) < 0)
+ return (-1);
+
+ IS_ERR(bind(ps->s, (struct sockaddr *)&sa, sizeof(sa)));
+
+ return (0);
+}
+
+
+ int
+procket_pipe(PROCKET_STATE *ps)
+{
+ struct sockaddr_un sa = { 0 };
+ int s = -1;
+
+
+ (void)memcpy(sa.sun_path, ps->path, sizeof(sa.sun_path)-1);
+
+ sa.sun_family = AF_UNIX;
+
+ IS_LTZERO(s = socket(PF_LOCAL, SOCK_STREAM, 0));
+ if (connect(s, (struct sockaddr *)&sa, sizeof(sa)) < 0)
+ err(EXIT_FAILURE, "connect");
+
+ if (ancil_send_fd(s, ps->s) < 0)
+ err(EXIT_FAILURE, "ancil_send_fd");
+ close (s);
+
+ return (0);
+}
+
+
+ void
+usage(PROCKET_STATE *ps)
+{
+ (void)fprintf(stderr, "%s, %s\n", __progname, PROCKET_VERSION);
+ (void)fprintf(stderr,
+ "usage: %s <options> <port|ipaddress:port>\n"
+ " -p <pipe> path to pipe file\n"
+ " -P <protocol> protocol number [tcp:6,udp:17]\n"
+ " -v verbose mode\n",
+ __progname
+ );
+
+ exit (EXIT_FAILURE);
+}
+
+
74 src/echo.erl
@@ -0,0 +1,74 @@
+%% Copyright (c) 2010, Michael Santos <michael.santos@gmail.com>
+%% All rights reserved.
+%%
+%% Redistribution and use in source and binary forms, with or without
+%% modification, are permitted provided that the following conditions
+%% are met:
+%%
+%% Redistributions of source code must retain the above copyright
+%% notice, this list of conditions and the following disclaimer.
+%%
+%% Redistributions in binary form must reproduce the above copyright
+%% notice, this list of conditions and the following disclaimer in the
+%% documentation and/or other materials provided with the distribution.
+%%
+%% Neither the name of the author nor the names of its contributors
+%% may be used to endorse or promote products derived from this software
+%% without specific prior written permission.
+%%
+%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+%% "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+%% LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+%% FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+%% COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+%% INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+%% BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+%% LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+%% CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+%% LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+%% ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+%% POSSIBILITY OF SUCH DAMAGE.
+
+%% procket test client
+-module(echo).
+
+-export([start/0,start/2]).
+
+-define(PORT, 9990).
+
+start() ->
+ start(?PORT, [{protocol, tcp}]).
+start(Port, Options) ->
+ Protocol = proplists:get_value(protocol, Options, tcp),
+ {ok, Fd} = procket:listen(Port, Options),
+ io:format("Listening on: ~p/~p~n", [Port, Protocol]),
+ listen(Protocol, Fd, Port).
+
+listen(tcp, Fd, Port) ->
+ {ok, S} = gen_tcp:listen(Port, [binary, {packet, 0}, {fd, Fd}]),
+ accept(S);
+listen(udp, Fd, Port) ->
+ {ok, S} = gen_udp:open(Port, [binary, {fd, Fd}]),
+ recv(S).
+
+accept(LS) ->
+ {ok, S} = gen_tcp:accept(LS),
+ spawn(fun() -> accept(LS) end),
+ recv(S).
+
+recv(S) ->
+ inet:setopts(S, [{active, once}]),
+ receive
+ {tcp, S, Data} ->
+ gen_tcp:send(S, Data),
+ recv(S);
+ {tcp_closed, S} ->
+ ok;
+ {udp, S, IP, Port, Data} ->
+ gen_udp:send(S, IP, Port, Data),
+ recv(S);
+ Error ->
+ error_logger:error_report({recv, Error})
+ end.
+
+
75 src/mktmp.erl
@@ -0,0 +1,75 @@
+%% Copyright (c) 2010, Michael Santos <michael.santos@gmail.com>
+%% All rights reserved.
+%%
+%% Redistribution and use in source and binary forms, with or without
+%% modification, are permitted provided that the following conditions
+%% are met:
+%%
+%% Redistributions of source code must retain the above copyright
+%% notice, this list of conditions and the following disclaimer.
+%%
+%% Redistributions in binary form must reproduce the above copyright
+%% notice, this list of conditions and the following disclaimer in the
+%% documentation and/or other materials provided with the distribution.
+%%
+%% Neither the name of the author nor the names of its contributors
+%% may be used to endorse or promote products derived from this software
+%% without specific prior written permission.
+%%
+%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+%% "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+%% LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+%% FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+%% COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+%% INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+%% BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+%% LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+%% CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+%% LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+%% ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+%% POSSIBILITY OF SUCH DAMAGE.
+
+%% Atomically create a temporary file.
+%%
+%% This module just creates a directory, put whatever temp files
+%% you %% need inside the directory.
+%%
+%% Maybe this is better to do in C, inside an NIF.
+%%
+%% Problems:
+%% * Aside from calling umask before running the VM, there
+%% doesn't appear to be a way of setting our permission
+%% mask. Generally, this will default to 022.
+%% * On some platforms, Unix sockets will be created with
+%% modes 777.
+%% * The module does not test if the directory exists before
+%% creation
+%% * If the directory creation fails, the module will just
+%% return an error.
+
+-module(mktmp).
+
+-export([dir/0,dir/1]).
+
+-define(TEMPLATELEN, 6).
+-define(TEMPNAME, "erlang.").
+
+dir() ->
+ TMP = case os:getenv("TMPDIR") of
+ false -> "/tmp";
+ Dir -> Dir
+ end,
+ dir(TMP).
+
+dir(TMP) ->
+ crypto:start(),
+ TmpDir = lists:flatten(
+ [ io_lib:format("~.16B", [N]) || N <- binary_to_list(crypto:rand_bytes(?TEMPLATELEN)) ]
+ ),
+ Path = TMP ++ "/" ++ ?TEMPNAME ++ TmpDir,
+ case file:make_dir(Path) of
+ ok -> Path;
+ Error -> Error
+ end.
+
+
94 src/procket.erl
@@ -0,0 +1,94 @@
+%% Copyright (c) 2010, Michael Santos <michael.santos@gmail.com>
+%% All rights reserved.
+%%
+%% Redistribution and use in source and binary forms, with or without
+%% modification, are permitted provided that the following conditions
+%% are met:
+%%
+%% Redistributions of source code must retain the above copyright
+%% notice, this list of conditions and the following disclaimer.
+%%
+%% Redistributions in binary form must reproduce the above copyright
+%% notice, this list of conditions and the following disclaimer in the
+%% documentation and/or other materials provided with the distribution.
+%%
+%% Neither the name of the author nor the names of its contributors
+%% may be used to endorse or promote products derived from this software
+%% without specific prior written permission.
+%%
+%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+%% "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+%% LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+%% FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+%% COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+%% INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+%% BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+%% LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+%% CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+%% LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+%% ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+%% POSSIBILITY OF SUCH DAMAGE.
+-module(procket).
+
+-export([init/0,open/2,poll/0,close/0,listen/1,listen/2]).
+-export([make_args/2]).
+
+-define(PROGNAME, "priv/procket").
+
+-on_load(on_load/0).
+
+init() ->
+ on_load().
+
+on_load() ->
+ ok = erlang:load_nif("priv/procket", []),
+ true.
+
+open(_,_) ->
+ erlang:error(not_implemented).
+
+poll() ->
+ erlang:error(not_implemented).
+
+close() ->
+ erlang:error(not_implemented).
+
+listen(Port) ->
+ listen(Port, []).
+listen(Port, Options) when is_integer(Port), is_list(Options) ->
+ Opt = case proplists:lookup(pipe, Options) of
+ none -> Options ++ [{pipe, mktmp:dir() ++ "/sock"}];
+ _ -> Options
+ end,
+ ok = open(proplists:get_value(pipe, Opt), Port),
+ Cmd = make_args(Port, Opt),
+ case os:cmd(Cmd) of
+ [] ->
+ poll();
+ Error ->
+ {error, procket_cmd, Error}
+ end.
+
+make_args(Port, Options) ->
+ Bind = " " ++ case proplists:lookup(ip, Options) of
+ none ->
+ integer_to_list(Port);
+ IP ->
+ get_switch(IP) ++ ":" ++ integer_to_list(Port)
+ end,
+ proplists:get_value(progname, Options, ?PROGNAME) ++ " " ++
+ string:join([ get_switch(proplists:lookup(Arg, Options)) || Arg <- [
+ pipe,
+ protocol
+ ], proplists:lookup(Arg, Options) /= none ],
+ " ") ++ Bind.
+
+get_switch({pipe, Arg}) -> "-p " ++ Arg;
+get_switch({protocol, raw}) -> "-P 0";
+get_switch({protocol, icmp}) -> "-P 1";
+get_switch({protocol, tcp}) -> "-P 6";
+get_switch({protocol, udp}) -> "-P 17";
+get_switch({ip, Arg}) when is_tuple(Arg) -> inet_parse:ntoa(Arg);
+get_switch({ip, Arg}) when is_list(Arg) -> Arg.
+
+

0 comments on commit 441fde8

Please sign in to comment.
Something went wrong with that request. Please try again.