summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt4
-rw-r--r--src/py_socket_wrapper.c883
-rw-r--r--src/socket.py50
-rw-r--r--src/socket_wrapper.c2666
-rw-r--r--src/socket_wrapper.h177
5 files changed, 3780 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..5c4aa29
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,4 @@
+project(libsocket_wrapper C)
+
+include_directories(${CMAKE_BINARY_DIR})
+add_library(socket_wrapper SHARED socket_wrapper.c)
diff --git a/src/py_socket_wrapper.c b/src/py_socket_wrapper.c
new file mode 100644
index 0000000..f5742d3
--- /dev/null
+++ b/src/py_socket_wrapper.c
@@ -0,0 +1,883 @@
+/*
+ * Copyright (C) Amitay Isaacs 2011
+ *
+ * All rights reserved.
+ *
+ * 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. 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 AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+/*
+ Python interface to socket wrapper library.
+
+ Passes all socket communication over unix domain sockets if the environment
+ variable SOCKET_WRAPPER_DIR is set.
+*/
+
+#include <Python.h>
+#include <pytalloc.h>
+#include "replace/replace.h"
+#include "system/network.h"
+#include "socket_wrapper.h"
+
+/* There's no Py_ssize_t in 2.4, apparently */
+#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 5
+typedef int Py_ssize_t;
+typedef inquiry lenfunc;
+typedef intargfunc ssizeargfunc;
+#endif
+
+#ifndef Py_RETURN_NONE
+#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
+#endif
+
+#ifndef Py_TYPE /* Py_TYPE is only available on Python > 2.6 */
+#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
+#endif
+
+#ifndef PY_CHECK_TYPE
+#define PY_CHECK_TYPE(type, var, fail) \
+ if (!PyObject_TypeCheck(var, type)) {\
+ PyErr_Format(PyExc_TypeError, __location__ ": Expected type '%s' for '%s' of type '%s'", (type)->tp_name, #var, Py_TYPE(var)->tp_name); \
+ fail; \
+ }
+#endif
+
+staticforward PyTypeObject PySocket;
+
+static PyObject *py_socket_error;
+
+void initsocket_wrapper(void);
+
+static PyObject *py_socket_addr_to_tuple(struct sockaddr *addr, socklen_t len)
+{
+ char host[256];
+ char service[8];
+ int status;
+ PyObject *pyaddr;
+
+ status = getnameinfo(addr, len, host, 255, service, 7, NI_NUMERICHOST|NI_NUMERICSERV);
+ if (status < 0) {
+ PyErr_SetString(py_socket_error, gai_strerror(status));
+ return NULL;
+ }
+
+ pyaddr = PyTuple_New(2);
+ if (pyaddr == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ PyTuple_SetItem(pyaddr, 0, PyString_FromString(host));
+ PyTuple_SetItem(pyaddr, 1, PyInt_FromLong(atoi(service)));
+
+ return pyaddr;
+}
+
+static bool py_socket_tuple_to_addr(PyObject *pyaddr, struct sockaddr *addr, socklen_t *len)
+{
+ const char *host;
+ char *service;
+ in_port_t port;
+ struct addrinfo *ainfo;
+ int status;
+
+ if (!PyTuple_Check(pyaddr)) {
+ PyErr_SetString(PyExc_TypeError, "Expected a tuple");
+ return false;
+ }
+
+ if (!PyArg_ParseTuple(pyaddr, "sH", &host, &port)) {
+ return false;
+ }
+
+ service = talloc_asprintf(NULL, "%d", port);
+ if (service == NULL) {
+ PyErr_NoMemory();
+ return false;
+ }
+
+ status = getaddrinfo(host, service, NULL, &ainfo);
+ if (status < 0) {
+ talloc_free(service);
+ PyErr_SetString(py_socket_error, gai_strerror(status));
+ return false;
+ }
+
+ talloc_free(service);
+
+ memcpy(addr, ainfo->ai_addr, sizeof(struct sockaddr));
+ *len = ainfo->ai_addrlen;
+
+ freeaddrinfo(ainfo);
+ return true;
+}
+
+
+static PyObject *py_socket_accept(pytalloc_Object *self, PyObject *args)
+{
+ int *sock, *new_sock;
+ struct sockaddr addr;
+ socklen_t addrlen;
+ PyObject *pysocket;
+ PyObject *pyaddr;
+ PyObject *pyret;
+
+ sock = pytalloc_get_ptr(self);
+
+ new_sock = talloc_zero(NULL, int);
+ if (new_sock == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ *new_sock = swrap_accept(*sock, &addr, &addrlen);
+ if (*new_sock < 0) {
+ return PyErr_SetFromErrno(py_socket_error);
+ }
+
+ if ((pysocket = pytalloc_steal(&PySocket, new_sock)) == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ pyret = PyTuple_New(2);
+ if (pyret == NULL) {
+ Py_DECREF(pysocket);
+ return PyErr_NoMemory();
+ }
+
+ pyaddr = py_socket_addr_to_tuple(&addr, addrlen);
+ if (pyaddr == NULL) {
+ Py_DECREF(pysocket);
+ Py_DECREF(pysocket);
+ return NULL;
+ }
+
+ PyTuple_SetItem(pyret, 0, pysocket);
+ PyTuple_SetItem(pyret, 1, pyaddr);
+ return pyret;
+}
+
+static PyObject *py_socket_bind(pytalloc_Object *self, PyObject *args)
+{
+ PyObject *pyaddr;
+ int *sock;
+ int status;
+ struct sockaddr addr;
+ socklen_t addrlen;
+
+ if (!PyArg_ParseTuple(args, "O:bind", &pyaddr)) {
+ return NULL;
+ }
+
+ if (!py_socket_tuple_to_addr(pyaddr, &addr, &addrlen)) {
+ return NULL;
+ }
+
+ sock = pytalloc_get_ptr(self);
+
+ status = swrap_bind(*sock, &addr, addrlen);
+ if (status < 0) {
+ PyErr_SetString(py_socket_error, "Unable to bind");
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_socket_close(pytalloc_Object *self, PyObject *args)
+{
+ int *sock;
+ int status;
+
+ sock = pytalloc_get_ptr(self);
+
+ status = swrap_close(*sock);
+ if (status < 0) {
+ return PyErr_SetFromErrno(py_socket_error);
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_socket_connect(pytalloc_Object *self, PyObject *args)
+{
+ int *sock;
+ PyObject *pyaddr;
+ struct sockaddr addr;
+ socklen_t addrlen;
+ int status;
+
+ if (!PyArg_ParseTuple(args, "O:connect", &pyaddr)) {
+ return NULL;
+ }
+
+ if (!py_socket_tuple_to_addr(pyaddr, &addr, &addrlen)) {
+ return NULL;
+ }
+
+ sock = pytalloc_get_ptr(self);
+
+ status = swrap_connect(*sock, &addr, addrlen);
+ if (status < 0) {
+ PyErr_SetFromErrno(py_socket_error);
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_socket_connect_ex(pytalloc_Object *self, PyObject *args)
+{
+ int *sock;
+ PyObject *pyaddr;
+ struct sockaddr addr;
+ socklen_t addrlen;
+ int status;
+
+ if (!PyArg_ParseTuple(args, "O:connect", &pyaddr)) {
+ return NULL;
+ }
+
+ if (!py_socket_tuple_to_addr(pyaddr, &addr, &addrlen)) {
+ return NULL;
+ }
+
+ sock = pytalloc_get_ptr(self);
+
+ status = swrap_connect(*sock, &addr, addrlen);
+ if (status < 0) {
+ return Py_BuildValue("%d", errno);
+ }
+
+ return Py_BuildValue("%d", 0);
+}
+
+static PyObject *py_socket_dup(pytalloc_Object *self, PyObject *args)
+{
+ int *sock, *new_sock;
+ PyObject *pysocket;
+
+ sock = pytalloc_get_ptr(self);
+
+ new_sock = talloc_zero(NULL, int);
+ if (new_sock == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ *new_sock = swrap_dup(*sock);
+ if (*new_sock < 0) {
+ return PyErr_SetFromErrno(py_socket_error);
+ }
+
+ pysocket = pytalloc_steal(&PySocket, new_sock);
+ if (pysocket == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ return pysocket;
+}
+
+static PyObject *py_socket_dup2(pytalloc_Object *self, PyObject *args)
+{
+ int *sock, *new_sock;
+ PyObject *pysocket;
+ int status;
+
+ if (!PyArg_ParseTuple(args, "O", &pysocket)) {
+ return NULL;
+ }
+
+ PY_CHECK_TYPE(&PySocket, pysocket, return NULL);
+
+ sock = pytalloc_get_ptr(self);
+ new_sock = pytalloc_get_ptr(pysocket);
+
+ status = swrap_dup2(*sock, *new_sock);
+ if (status < 0) {
+ return PyErr_SetFromErrno(py_socket_error);
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_socket_fileno(pytalloc_Object *self, PyObject *args)
+{
+ PyErr_SetString(py_socket_error, "Not Supported");
+ return NULL;
+}
+
+static PyObject *py_socket_getpeername(pytalloc_Object *self, PyObject *args)
+{
+ int *sock;
+ struct sockaddr addr;
+ socklen_t addrlen;
+ int status;
+ PyObject *pyaddr;
+
+ sock = pytalloc_get_ptr(self);
+
+ status = swrap_getpeername(*sock, &addr, &addrlen);
+ if (status < 0) {
+ return PyErr_SetFromErrno(py_socket_error);
+ }
+
+ pyaddr = py_socket_addr_to_tuple(&addr, addrlen);
+
+ return pyaddr;
+}
+
+static PyObject *py_socket_getsockname(pytalloc_Object *self, PyObject *args)
+{
+ int *sock;
+ struct sockaddr addr;
+ socklen_t addrlen;
+ int status;
+ PyObject *pyaddr;
+
+ sock = pytalloc_get_ptr(self);
+
+ status = swrap_getsockname(*sock, &addr, &addrlen);
+ if (status < 0) {
+ return PyErr_SetFromErrno(py_socket_error);
+ }
+
+ pyaddr = py_socket_addr_to_tuple(&addr, addrlen);
+
+ return pyaddr;
+}
+
+static PyObject *py_socket_getsockopt(pytalloc_Object *self, PyObject *args)
+{
+ int level, optname;
+ int *sock;
+ socklen_t optlen = 0, newlen;
+ int optval;
+ bool is_integer = false;
+ char *buffer;
+ PyObject *pyret;
+ int status;
+
+ if (!PyArg_ParseTuple(args, "ii|i:getsockopt", &level, &optname, &optlen)) {
+ return NULL;
+ }
+
+ if (optlen == 0) {
+ optlen = sizeof(int);
+ is_integer = true;
+ }
+
+ buffer = talloc_zero_array(NULL, char, optlen);
+ if (buffer == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ sock = pytalloc_get_ptr(self);
+
+ status = swrap_getsockopt(*sock, level, optname, (void *)buffer, &newlen);
+ if (status < 0) {
+ talloc_free(buffer);
+ return PyErr_SetFromErrno(py_socket_error);
+ }
+
+ if (is_integer) {
+ optval = *(int *)buffer;
+ pyret = PyInt_FromLong(optval);
+ } else {
+ pyret = PyString_FromStringAndSize(buffer, optlen);
+ }
+
+ talloc_free(buffer);
+
+ return pyret;
+}
+
+static PyObject *py_socket_gettimeout(pytalloc_Object *self, PyObject *args)
+{
+ PyErr_SetString(py_socket_error, "Not Supported");
+ return NULL;
+}
+
+static PyObject *py_socket_listen(pytalloc_Object *self, PyObject *args)
+{
+ int backlog;
+ int *sock;
+ int status;
+
+ if (!PyArg_ParseTuple(args, "i:listen", &backlog)) {
+ return NULL;
+ }
+
+ sock = pytalloc_get_ptr(self);
+
+ status = swrap_listen(*sock, backlog);
+ if (status < 0) {
+ return PyErr_SetFromErrno(py_socket_error);
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_socket_makefile(pytalloc_Object *self, PyObject *args)
+{
+ PyErr_SetString(py_socket_error, "Not Supported");
+ return NULL;
+}
+
+static PyObject *py_socket_read(pytalloc_Object *self, PyObject *args)
+{
+ int bufsize, len;
+ int *sock;
+ char *buffer;
+ PyObject *pyret;
+
+ if (!PyArg_ParseTuple(args, "i:read", &bufsize)) {
+ return NULL;
+ }
+
+ buffer = talloc_zero_array(NULL, char, bufsize);
+ if (buffer == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ sock = pytalloc_get_ptr(self);
+
+ len = swrap_read(*sock, buffer, bufsize);
+ if (len < 0) {
+ return PyErr_SetFromErrno(py_socket_error);
+ }
+
+ pyret = PyString_FromStringAndSize(buffer, len);
+
+ talloc_free(buffer);
+
+ return pyret;
+}
+
+static PyObject *py_socket_recv(pytalloc_Object *self, PyObject *args)
+{
+ int bufsize, flags, len;
+ int *sock;
+ char *buffer;
+ PyObject *pyret;
+
+ if (!PyArg_ParseTuple(args, "ii:recv", &bufsize, &flags)) {
+ return NULL;
+ }
+
+ buffer = talloc_zero_array(NULL, char, bufsize);
+ if (buffer == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ sock = pytalloc_get_ptr(self);
+
+ len = swrap_recv(*sock, buffer, bufsize, flags);
+ if (len < 0) {
+ return PyErr_SetFromErrno(py_socket_error);
+ }
+
+ pyret = PyString_FromStringAndSize(buffer, len);
+
+ talloc_free(buffer);
+
+ return pyret;
+}
+
+static PyObject *py_socket_recvfrom(pytalloc_Object *self, PyObject *args)
+{
+ int bufsize, flags, len;
+ int *sock;
+ char *buffer;
+ struct sockaddr from;
+ socklen_t fromlen;
+ PyObject *pybuf, *pyaddr, *pyret;
+
+ if (!PyArg_ParseTuple(args, "ii:recvfrom", &bufsize, &flags)) {
+ return NULL;
+ }
+
+ buffer = talloc_zero_array(NULL, char, bufsize);
+ if (buffer == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ sock = pytalloc_get_ptr(self);
+
+ fromlen = sizeof(struct sockaddr);
+
+ len = swrap_recvfrom(*sock, buffer, bufsize, flags, &from, &fromlen);
+ if (len < 0) {
+ talloc_free(buffer);
+ return PyErr_SetFromErrno(py_socket_error);
+ }
+
+ pybuf = PyString_FromStringAndSize(buffer, len);
+ if (pybuf == NULL) {
+ talloc_free(buffer);
+ return PyErr_NoMemory();
+ }
+
+ talloc_free(buffer);
+
+ pyaddr = py_socket_addr_to_tuple(&from, fromlen);
+ if (pyaddr == NULL) {
+ Py_DECREF(pybuf);
+ return NULL;
+ }
+
+ pyret = PyTuple_New(2);
+ if (pyret == NULL) {
+ Py_DECREF(pybuf);
+ Py_DECREF(pyaddr);
+ return PyErr_NoMemory();
+ }
+
+ PyTuple_SetItem(pyret, 0, pybuf);
+ PyTuple_SetItem(pyret, 1, pyaddr);
+
+ return pyret;
+}
+
+static PyObject *py_socket_send(pytalloc_Object *self, PyObject *args)
+{
+ char *buffer;
+ int len, flags;
+ int *sock;
+ int status;
+
+ if (!PyArg_ParseTuple(args, "s#i:sendto", &buffer, &len, &flags)) {
+ return NULL;
+ }
+
+ sock = pytalloc_get_ptr(self);
+
+ status = swrap_send(*sock, buffer, len, flags);
+ if (status < 0) {
+ PyErr_SetFromErrno(py_socket_error);
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_socket_sendall(pytalloc_Object *self, PyObject *args)
+{
+ char *buffer;
+ int len, flags;
+ int *sock;
+ int status;
+
+ if (!PyArg_ParseTuple(args, "s#i:sendall", &buffer, &len, &flags)) {
+ return NULL;
+ }
+
+ sock = pytalloc_get_ptr(self);
+
+ status = swrap_send(*sock, buffer, len, flags);
+ if (status < 0) {
+ PyErr_SetFromErrno(py_socket_error);
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_socket_sendto(pytalloc_Object *self, PyObject *args)
+{
+ PyObject *pyaddr;
+ char *buffer;
+ int len, flags;
+ int *sock;
+ struct sockaddr addr;
+ socklen_t addrlen;
+ int status;
+
+ if (!PyArg_ParseTuple(args, "s#iO:sendto", &buffer, &len, &flags, &pyaddr)) {
+ return NULL;
+ }
+
+ if (!py_socket_tuple_to_addr(pyaddr, &addr, &addrlen)) {
+ return NULL;
+ }
+
+ sock = pytalloc_get_ptr(self);
+
+ status = swrap_sendto(*sock, buffer, len, flags, &addr, addrlen);
+ if (status < 0) {
+ PyErr_SetFromErrno(py_socket_error);
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_socket_setblocking(pytalloc_Object *self, PyObject *args)
+{
+ PyErr_SetString(py_socket_error, "Not Supported");
+ return NULL;
+}
+
+static PyObject *py_socket_setsockopt(pytalloc_Object *self, PyObject *args)
+{
+ int level, optname;
+ int *sock;
+ PyObject *pyval;
+ int optval;
+ Py_ssize_t optlen;
+ char *buffer;
+ int status;
+
+ if (!PyArg_ParseTuple(args, "iiO:getsockopt", &level, &optname, &pyval)) {
+ return NULL;
+ }
+
+ if (PyInt_Check(pyval)) {
+ optval = PyInt_AsLong(pyval);
+ buffer = (char *)&optval;
+ optlen = sizeof(int);
+ } else {
+ PyString_AsStringAndSize(pyval, &buffer, &optlen);
+ }
+
+ sock = pytalloc_get_ptr(self);
+
+ status = swrap_setsockopt(*sock, level, optname, (void *)buffer, optlen);
+ if (status < 0) {
+ return PyErr_SetFromErrno(py_socket_error);
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_socket_settimeout(pytalloc_Object *self, PyObject *args)
+{
+ PyErr_SetString(py_socket_error, "Not Supported");
+ return NULL;
+}
+
+static PyObject *py_socket_shutdown(pytalloc_Object *self, PyObject *args)
+{
+ PyErr_SetString(py_socket_error, "Not Supported");
+ return NULL;
+}
+
+static PyObject *py_socket_write(pytalloc_Object *self, PyObject *args)
+{
+ char *buffer;
+ int len;
+ int *sock;
+ int status;
+
+ if (!PyArg_ParseTuple(args, "s#:write", &buffer, &len)) {
+ return NULL;
+ }
+
+ sock = pytalloc_get_ptr(self);
+
+ status = swrap_send(*sock, buffer, len, 0);
+ if (status < 0) {
+ PyErr_SetFromErrno(py_socket_error);
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+
+static PyMethodDef py_socket_methods[] = {
+ { "accept", (PyCFunction)py_socket_accept, METH_NOARGS,
+ "accept() -> (socket object, address info)\n\n \
+ Wait for an incoming connection." },
+ { "bind", (PyCFunction)py_socket_bind, METH_VARARGS,
+ "bind(address)\n\n \
+ Bind the socket to a local address." },
+ { "close", (PyCFunction)py_socket_close, METH_NOARGS,
+ "close()\n\n \
+ Close the socket." },
+ { "connect", (PyCFunction)py_socket_connect, METH_VARARGS,
+ "connect(address)\n\n \
+ Connect the socket to a remote address." },
+ { "connect_ex", (PyCFunction)py_socket_connect_ex, METH_VARARGS,
+ "connect_ex(address)\n\n \
+ Connect the socket to a remote address." },
+ { "dup", (PyCFunction)py_socket_dup, METH_VARARGS,
+ "dup() -> socket object\n\n \
+ Return a new socket object connected to the same system resource." },
+ { "dup2", (PyCFunction)py_socket_dup2, METH_VARARGS,
+ "dup2(socket object) -> socket object\n\n \
+ Return a new socket object connected to teh same system resource." },
+ { "fileno", (PyCFunction)py_socket_fileno, METH_NOARGS,
+ "fileno() -> file descriptor\n\n \
+ Return socket's file descriptor." },
+ { "getpeername", (PyCFunction)py_socket_getpeername, METH_NOARGS,
+ "getpeername() -> address info\n\n \
+ Return the address of the remote endpoint." },
+ { "getsockname", (PyCFunction)py_socket_getsockname, METH_NOARGS,
+ "getsockname() -> address info\n\n \
+ Return the address of the local endpoing." },
+ { "getsockopt", (PyCFunction)py_socket_getsockopt, METH_VARARGS,
+ "getsockopt(level, option[, buffersize]) -> value\n\n \
+ Get a socket option." },
+ { "gettimeout", (PyCFunction)py_socket_gettimeout, METH_NOARGS,
+ "gettimeout() -> value\n\n \
+ Return the timeout in seconds associated with socket operations." },
+ { "listen", (PyCFunction)py_socket_listen, METH_VARARGS,
+ "listen(backlog)\n\n \
+ Enable a server to accept connections." },
+ { "makefile", (PyCFunction)py_socket_makefile, METH_NOARGS,
+ "makefile() -> file object\n\n \
+ Return a file object associated with the socket." },
+ { "read", (PyCFunction)py_socket_read, METH_VARARGS,
+ "read(buflen) -> data\n\n \
+ Receive data." },
+ { "recv", (PyCFunction)py_socket_recv, METH_VARARGS,
+ "recv(buflen, flags) -> data\n\n \
+ Receive data." },
+ { "recvfrom", (PyCFunction)py_socket_recvfrom, METH_VARARGS,
+ "recvfrom(buflen, flags) -> (data, sender address)\n\n \
+ Receive data and sender's address." },
+ { "send", (PyCFunction)py_socket_send, METH_VARARGS,
+ "send(data, flags)\n\n \
+ Send data." },
+ { "sendall", (PyCFunction)py_socket_sendall, METH_VARARGS,
+ "sendall(data, flags)\n\n \
+ Send data." },
+ { "sendto", (PyCFunction)py_socket_sendto, METH_VARARGS,
+ "sendto(data, flags, addr)\n\n \
+ Send data to a given address." },
+ { "setblocking", (PyCFunction)py_socket_setblocking, METH_VARARGS,
+ "setblocking(flag)\n\n \
+ Set blocking or non-blocking mode of the socket." },
+ { "setsockopt", (PyCFunction)py_socket_setsockopt, METH_VARARGS,
+ "setsockopt(level, option, value)\n\n \
+ Set a socket option." },
+ { "settimeout", (PyCFunction)py_socket_settimeout, METH_VARARGS,
+ "settimeout(value)\n\n \
+ Set a timeout on socket blocking operations." },
+ { "shutdown", (PyCFunction)py_socket_shutdown, METH_VARARGS,
+ "shutdown(how)\n\n \
+ Shut down one or both halves of the connection." },
+ { "write", (PyCFunction)py_socket_write, METH_VARARGS,
+ "write(data)\n\n \
+ Send data." },
+ { NULL },
+};
+
+
+static PyObject *py_socket_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ int family, sock_type, protocol;
+ int *sock;
+ PyObject *pysocket;
+
+ if (!PyArg_ParseTuple(args, "iii:socket", &family, &sock_type, &protocol)) {
+ return NULL;
+ }
+
+ sock = talloc_zero(NULL, int);
+ if (sock == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ *sock = swrap_socket(family, sock_type, protocol);
+ if (*sock < 0) {
+ return PyErr_SetFromErrno(py_socket_error);
+ }
+
+ if ((pysocket = pytalloc_steal(type, sock)) == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ return pysocket;
+}
+
+
+static PyTypeObject PySocket = {
+ .tp_name = "socket_wrapper.socket",
+ .tp_basicsize = sizeof(pytalloc_Object),
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_methods = py_socket_methods,
+ .tp_new = py_socket_new,
+ .tp_doc = "socket(family, type, proto) -> socket object\n\n Open a socket of the give type.",
+};
+
+static PyObject *py_socket_wrapper_dir(PyObject *self)
+{
+ const char *dir;
+
+ dir = socket_wrapper_dir();
+
+ return PyString_FromString(dir);
+}
+
+static PyObject *py_socket_wrapper_default_interface(PyObject *self)
+{
+ unsigned int id;
+
+ id = socket_wrapper_default_iface();
+
+ return PyInt_FromLong(id);
+}
+
+
+static PyMethodDef py_socket_wrapper_methods[] = {
+ { "dir", (PyCFunction)py_socket_wrapper_dir, METH_NOARGS,
+ "dir() -> path\n\n \
+ Return socket_wrapper directory." },
+ { "default_iface", (PyCFunction)py_socket_wrapper_default_interface, METH_NOARGS,
+ "default_iface() -> id\n\n \
+ Return default interface id." },
+ { NULL },
+};
+
+void initsocket_wrapper(void)
+{
+ PyObject *m;
+ char exception_name[] = "socket_wrapper.error";
+
+ PyTypeObject *talloc_type = pytalloc_GetObjectType();
+ if (talloc_type == NULL) {
+ return;
+ }
+
+ PySocket.tp_base = talloc_type;
+ if (PyType_Ready(&PySocket) < 0) {
+ return;
+ }
+
+ m = Py_InitModule3("socket_wrapper", py_socket_wrapper_methods, "Socket wrapper");
+ if (m == NULL) {
+ return;
+ }
+
+ py_socket_error = PyErr_NewException(exception_name, NULL, NULL);
+ Py_INCREF(py_socket_error);
+ PyModule_AddObject(m, "error", py_socket_error);
+
+ Py_INCREF(&PySocket);
+ PyModule_AddObject(m, "socket", (PyObject *)&PySocket);
+}
diff --git a/src/socket.py b/src/socket.py
new file mode 100644
index 0000000..ccbb820
--- /dev/null
+++ b/src/socket.py
@@ -0,0 +1,50 @@
+# Wrapper for socket wrapper (based on python socket wrapper)
+# Copyright (C) Amitay Isaacs 2011
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import _socket
+from _socket import *
+
+from samba.socket_wrapper import socket
+
+
+def getfqdn(name=''):
+ """Get fully qualified domain name from name.
+
+ An empty argument is interpreted as meaning the local host.
+
+ First the hostname returned by gethostbyaddr() is checked, then
+ possibly existing aliases. In case no FQDN is available, hostname
+ from gethostname() is returned.
+ """
+ name = name.strip()
+ if not name or name == '0.0.0.0':
+ name = gethostname()
+ try:
+ hostname, aliases, ipaddrs = gethostbyaddr(name)
+ except error:
+ pass
+ else:
+ aliases.insert(0, hostname)
+ for name in aliases:
+ if '.' in name:
+ break
+ else:
+ name = hostname
+ return name
+
+
+_GLOBAL_DEFAULT_TIMEOUT = object()
diff --git a/src/socket_wrapper.c b/src/socket_wrapper.c
new file mode 100644
index 0000000..7cf09a8
--- /dev/null
+++ b/src/socket_wrapper.c
@@ -0,0 +1,2666 @@
+/*
+ * Copyright (C) Jelmer Vernooij 2005,2008 <jelmer@samba.org>
+ * Copyright (C) Stefan Metzmacher 2006-2009 <metze@samba.org>
+ * Copyright (C) Andreas Schneider 2013 <asn@samba.org>
+ *
+ * All rights reserved.
+ *
+ * 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. 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 AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+/*
+ Socket wrapper library. Passes all socket communication over
+ unix domain sockets if the environment variable SOCKET_WRAPPER_DIR
+ is set.
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+#include <errno.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#ifndef _PUBLIC_
+#define _PUBLIC_
+#endif
+
+#define SWRAP_DLIST_ADD(list,item) do { \
+ if (!(list)) { \
+ (item)->prev = NULL; \
+ (item)->next = NULL; \
+ (list) = (item); \
+ } else { \
+ (item)->prev = NULL; \
+ (item)->next = (list); \
+ (list)->prev = (item); \
+ (list) = (item); \
+ } \
+} while (0)
+
+#define SWRAP_DLIST_REMOVE(list,item) do { \
+ if ((list) == (item)) { \
+ (list) = (item)->next; \
+ if (list) { \
+ (list)->prev = NULL; \
+ } \
+ } else { \
+ if ((item)->prev) { \
+ (item)->prev->next = (item)->next; \
+ } \
+ if ((item)->next) { \
+ (item)->next->prev = (item)->prev; \
+ } \
+ } \
+ (item)->prev = NULL; \
+ (item)->next = NULL; \
+} while (0)
+
+#ifdef HAVE_GETTIMEOFDAY_TZ
+#define swrapGetTimeOfDay(tval) gettimeofday(tval,NULL)
+#else
+#define swrapGetTimeOfDay(tval) gettimeofday(tval)
+#endif
+
+/* we need to use a very terse format here as IRIX 6.4 silently
+ truncates names to 16 chars, so if we use a longer name then we
+ can't tell which port a packet came from with recvfrom()
+
+ with this format we have 8 chars left for the directory name
+*/
+#define SOCKET_FORMAT "%c%02X%04X"
+#define SOCKET_TYPE_CHAR_TCP 'T'
+#define SOCKET_TYPE_CHAR_UDP 'U'
+#define SOCKET_TYPE_CHAR_TCP_V6 'X'
+#define SOCKET_TYPE_CHAR_UDP_V6 'Y'
+
+/* This limit is to avoid broadcast sendto() needing to stat too many
+ * files. It may be raised (with a performance cost) to up to 254
+ * without changing the format above */
+#define MAX_WRAPPED_INTERFACES 40
+
+#include <dlfcn.h>
+
+#define LIBC_NAME "libc.so.6"
+
+static void *libc_hnd;
+
+static int libc_dlopen(void)
+{
+ if (libc_hnd != NULL) {
+ return 0;
+ }
+ libc_hnd = dlopen(LIBC_NAME, RTLD_LAZY);
+ if (libc_hnd == NULL) {
+ printf("Failed to dlopen %s: %s\n", LIBC_NAME, dlerror());
+ exit(-1);
+ }
+
+ return 0;
+}
+
+static void *libc_dlsym(const char *name)
+{
+ void *func;
+
+ libc_dlopen();
+
+ func = dlsym(libc_hnd, name);
+
+ if (func == NULL) {
+ printf("Failed to find %s in %s: %s\n",
+ name, LIBC_NAME, dlerror());
+ exit(-1);
+ }
+
+ return func;
+}
+
+static int (*libc_accept)(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
+
+static int real_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
+{
+ if (libc_accept == NULL) {
+ *(void **)(&libc_accept) = libc_dlsym("accept");
+ }
+
+ return libc_accept(sockfd, addr, addrlen);
+}
+
+#ifdef HAVE_IPV6
+/*
+ * FD00::5357:5FXX
+ */
+static const struct in6_addr *swrap_ipv6(void)
+{
+ static struct in6_addr v;
+ static int initialized;
+ int ret;
+
+ if (initialized) {
+ return &v;
+ }
+ initialized = 1;
+
+ ret = inet_pton(AF_INET6, "FD00::5357:5F00", &v);
+ if (ret <= 0) {
+ abort();
+ }
+
+ return &v;
+}
+#endif
+
+static struct sockaddr *sockaddr_dup(const void *data, socklen_t len)
+{
+ struct sockaddr *ret = (struct sockaddr *)malloc(len);
+ memcpy(ret, data, len);
+ return ret;
+}
+
+static void set_port(int family, int prt, struct sockaddr *addr)
+{
+ switch (family) {
+ case AF_INET:
+ ((struct sockaddr_in *)addr)->sin_port = htons(prt);
+ break;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ ((struct sockaddr_in6 *)addr)->sin6_port = htons(prt);
+ break;
+#endif
+ }
+}
+
+static size_t socket_length(int family)
+{
+ switch (family) {
+ case AF_INET:
+ return sizeof(struct sockaddr_in);
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ return sizeof(struct sockaddr_in6);
+#endif
+ }
+ return 0;
+}
+
+struct socket_info_fd {
+ struct socket_info_fd *prev, *next;
+ int fd;
+};
+
+struct socket_info
+{
+ struct socket_info_fd *fds;
+
+ int family;
+ int type;
+ int protocol;
+ int bound;
+ int bcast;
+ int is_server;
+ int connected;
+ int defer_connect;
+
+ char *tmp_path;
+
+ struct sockaddr *myname;
+ socklen_t myname_len;
+
+ struct sockaddr *peername;
+ socklen_t peername_len;
+
+ struct {
+ unsigned long pck_snd;
+ unsigned long pck_rcv;
+ } io;
+
+ struct socket_info *prev, *next;
+};
+
+static struct socket_info *sockets;
+
+const char *socket_wrapper_dir(void)
+{
+ const char *s = getenv("SOCKET_WRAPPER_DIR");
+ if (s == NULL) {
+ return NULL;
+ }
+ if (strncmp(s, "./", 2) == 0) {
+ s += 2;
+ }
+ return s;
+}
+
+unsigned int socket_wrapper_default_iface(void)
+{
+ const char *s = getenv("SOCKET_WRAPPER_DEFAULT_IFACE");
+ if (s) {
+ unsigned int iface;
+ if (sscanf(s, "%u", &iface) == 1) {
+ if (iface >= 1 && iface <= MAX_WRAPPED_INTERFACES) {
+ return iface;
+ }
+ }
+ }
+
+ return 1;/* 127.0.0.1 */
+}
+
+static int convert_un_in(const struct sockaddr_un *un, struct sockaddr *in, socklen_t *len)
+{
+ unsigned int iface;
+ unsigned int prt;
+ const char *p;
+ char type;
+
+ p = strrchr(un->sun_path, '/');
+ if (p) p++; else p = un->sun_path;
+
+ if (sscanf(p, SOCKET_FORMAT, &type, &iface, &prt) != 3) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (iface == 0 || iface > MAX_WRAPPED_INTERFACES) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (prt > 0xFFFF) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ switch(type) {
+ case SOCKET_TYPE_CHAR_TCP:
+ case SOCKET_TYPE_CHAR_UDP: {
+ struct sockaddr_in *in2 = (struct sockaddr_in *)(void *)in;
+
+ if ((*len) < sizeof(*in2)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(in2, 0, sizeof(*in2));
+ in2->sin_family = AF_INET;
+ in2->sin_addr.s_addr = htonl((127<<24) | iface);
+ in2->sin_port = htons(prt);
+
+ *len = sizeof(*in2);
+ break;
+ }
+#ifdef HAVE_IPV6
+ case SOCKET_TYPE_CHAR_TCP_V6:
+ case SOCKET_TYPE_CHAR_UDP_V6: {
+ struct sockaddr_in6 *in2 = (struct sockaddr_in6 *)(void *)in;
+
+ if ((*len) < sizeof(*in2)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(in2, 0, sizeof(*in2));
+ in2->sin6_family = AF_INET6;
+ in2->sin6_addr = *swrap_ipv6();
+ in2->sin6_addr.s6_addr[15] = iface;
+ in2->sin6_port = htons(prt);
+
+ *len = sizeof(*in2);
+ break;
+ }
+#endif
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int convert_in_un_remote(struct socket_info *si, const struct sockaddr *inaddr, struct sockaddr_un *un,
+ int *bcast)
+{
+ char type = '\0';
+ unsigned int prt;
+ unsigned int iface;
+ int is_bcast = 0;
+
+ if (bcast) *bcast = 0;
+
+ switch (inaddr->sa_family) {
+ case AF_INET: {
+ const struct sockaddr_in *in =
+ (const struct sockaddr_in *)(const void *)inaddr;
+ unsigned int addr = ntohl(in->sin_addr.s_addr);
+ char u_type = '\0';
+ char b_type = '\0';
+ char a_type = '\0';
+
+ switch (si->type) {
+ case SOCK_STREAM:
+ u_type = SOCKET_TYPE_CHAR_TCP;
+ break;
+ case SOCK_DGRAM:
+ u_type = SOCKET_TYPE_CHAR_UDP;
+ a_type = SOCKET_TYPE_CHAR_UDP;
+ b_type = SOCKET_TYPE_CHAR_UDP;
+ break;
+ }
+
+ prt = ntohs(in->sin_port);
+ if (a_type && addr == 0xFFFFFFFF) {
+ /* 255.255.255.255 only udp */
+ is_bcast = 2;
+ type = a_type;
+ iface = socket_wrapper_default_iface();
+ } else if (b_type && addr == 0x7FFFFFFF) {
+ /* 127.255.255.255 only udp */
+ is_bcast = 1;
+ type = b_type;
+ iface = socket_wrapper_default_iface();
+ } else if ((addr & 0xFFFFFF00) == 0x7F000000) {
+ /* 127.0.0.X */
+ is_bcast = 0;
+ type = u_type;
+ iface = (addr & 0x000000FF);
+ } else {
+ errno = ENETUNREACH;
+ return -1;
+ }
+ if (bcast) *bcast = is_bcast;
+ break;
+ }
+#ifdef HAVE_IPV6
+ case AF_INET6: {
+ const struct sockaddr_in6 *in =
+ (const struct sockaddr_in6 *)(const void *)inaddr;
+ struct in6_addr cmp1, cmp2;
+
+ switch (si->type) {
+ case SOCK_STREAM:
+ type = SOCKET_TYPE_CHAR_TCP_V6;
+ break;
+ case SOCK_DGRAM:
+ type = SOCKET_TYPE_CHAR_UDP_V6;
+ break;
+ }
+
+ /* XXX no multicast/broadcast */
+
+ prt = ntohs(in->sin6_port);
+
+ cmp1 = *swrap_ipv6();
+ cmp2 = in->sin6_addr;
+ cmp2.s6_addr[15] = 0;
+ if (IN6_ARE_ADDR_EQUAL(&cmp1, &cmp2)) {
+ iface = in->sin6_addr.s6_addr[15];
+ } else {
+ errno = ENETUNREACH;
+ return -1;
+ }
+
+ break;
+ }
+#endif
+ default:
+ errno = ENETUNREACH;
+ return -1;
+ }
+
+ if (prt == 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (is_bcast) {
+ snprintf(un->sun_path, sizeof(un->sun_path), "%s/EINVAL",
+ socket_wrapper_dir());
+ /* the caller need to do more processing */
+ return 0;
+ }
+
+ snprintf(un->sun_path, sizeof(un->sun_path), "%s/"SOCKET_FORMAT,
+ socket_wrapper_dir(), type, iface, prt);
+
+ return 0;
+}
+
+static int convert_in_un_alloc(struct socket_info *si, const struct sockaddr *inaddr, struct sockaddr_un *un,
+ int *bcast)
+{
+ char type = '\0';
+ unsigned int prt;
+ unsigned int iface;
+ struct stat st;
+ int is_bcast = 0;
+
+ if (bcast) *bcast = 0;
+
+ switch (si->family) {
+ case AF_INET: {
+ const struct sockaddr_in *in =
+ (const struct sockaddr_in *)(const void *)inaddr;
+ unsigned int addr = ntohl(in->sin_addr.s_addr);
+ char u_type = '\0';
+ char d_type = '\0';
+ char b_type = '\0';
+ char a_type = '\0';
+
+ prt = ntohs(in->sin_port);
+
+ switch (si->type) {
+ case SOCK_STREAM:
+ u_type = SOCKET_TYPE_CHAR_TCP;
+ d_type = SOCKET_TYPE_CHAR_TCP;
+ break;
+ case SOCK_DGRAM:
+ u_type = SOCKET_TYPE_CHAR_UDP;
+ d_type = SOCKET_TYPE_CHAR_UDP;
+ a_type = SOCKET_TYPE_CHAR_UDP;
+ b_type = SOCKET_TYPE_CHAR_UDP;
+ break;
+ }
+
+ if (addr == 0) {
+ /* 0.0.0.0 */
+ is_bcast = 0;
+ type = d_type;
+ iface = socket_wrapper_default_iface();
+ } else if (a_type && addr == 0xFFFFFFFF) {
+ /* 255.255.255.255 only udp */
+ is_bcast = 2;
+ type = a_type;
+ iface = socket_wrapper_default_iface();
+ } else if (b_type && addr == 0x7FFFFFFF) {
+ /* 127.255.255.255 only udp */
+ is_bcast = 1;
+ type = b_type;
+ iface = socket_wrapper_default_iface();
+ } else if ((addr & 0xFFFFFF00) == 0x7F000000) {
+ /* 127.0.0.X */
+ is_bcast = 0;
+ type = u_type;
+ iface = (addr & 0x000000FF);
+ } else {
+ errno = EADDRNOTAVAIL;
+ return -1;
+ }
+ break;
+ }
+#ifdef HAVE_IPV6
+ case AF_INET6: {
+ const struct sockaddr_in6 *in =
+ (const struct sockaddr_in6 *)(const void *)inaddr;
+ struct in6_addr cmp1, cmp2;
+
+ switch (si->type) {
+ case SOCK_STREAM:
+ type = SOCKET_TYPE_CHAR_TCP_V6;
+ break;
+ case SOCK_DGRAM:
+ type = SOCKET_TYPE_CHAR_UDP_V6;
+ break;
+ }
+
+ /* XXX no multicast/broadcast */
+
+ prt = ntohs(in->sin6_port);
+
+ cmp1 = *swrap_ipv6();
+ cmp2 = in->sin6_addr;
+ cmp2.s6_addr[15] = 0;
+ if (IN6_IS_ADDR_UNSPECIFIED(&in->sin6_addr)) {
+ iface = socket_wrapper_default_iface();
+ } else if (IN6_ARE_ADDR_EQUAL(&cmp1, &cmp2)) {
+ iface = in->sin6_addr.s6_addr[15];
+ } else {
+ errno = EADDRNOTAVAIL;
+ return -1;
+ }
+
+ break;
+ }
+#endif
+ default:
+ errno = EADDRNOTAVAIL;
+ return -1;
+ }
+
+
+ if (bcast) *bcast = is_bcast;
+
+ if (iface == 0 || iface > MAX_WRAPPED_INTERFACES) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (prt == 0) {
+ /* handle auto-allocation of ephemeral ports */
+ for (prt = 5001; prt < 10000; prt++) {
+ snprintf(un->sun_path, sizeof(un->sun_path), "%s/"SOCKET_FORMAT,
+ socket_wrapper_dir(), type, iface, prt);
+ if (stat(un->sun_path, &st) == 0) continue;
+
+ set_port(si->family, prt, si->myname);
+ break;
+ }
+ if (prt == 10000) {
+ errno = ENFILE;
+ return -1;
+ }
+ }
+
+ snprintf(un->sun_path, sizeof(un->sun_path), "%s/"SOCKET_FORMAT,
+ socket_wrapper_dir(), type, iface, prt);
+ return 0;
+}
+
+static struct socket_info *find_socket_info(int fd)
+{
+ struct socket_info *i;
+ for (i = sockets; i; i = i->next) {
+ struct socket_info_fd *f;
+ for (f = i->fds; f; f = f->next) {
+ if (f->fd == fd) {
+ return i;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static int sockaddr_convert_to_un(struct socket_info *si, const struct sockaddr *in_addr, socklen_t in_len,
+ struct sockaddr_un *out_addr, int alloc_sock, int *bcast)
+{
+ struct sockaddr *out = (struct sockaddr *)(void *)out_addr;
+ if (!out_addr)
+ return 0;
+
+ out->sa_family = AF_UNIX;
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ out->sa_len = sizeof(*out_addr);
+#endif
+
+ switch (in_addr->sa_family) {
+ case AF_INET:
+#ifdef HAVE_IPV6
+ case AF_INET6:
+#endif
+ switch (si->type) {
+ case SOCK_STREAM:
+ case SOCK_DGRAM:
+ break;
+ default:
+ errno = ESOCKTNOSUPPORT;
+ return -1;
+ }
+ if (alloc_sock) {
+ return convert_in_un_alloc(si, in_addr, out_addr, bcast);
+ } else {
+ return convert_in_un_remote(si, in_addr, out_addr, bcast);
+ }
+ default:
+ break;
+ }
+
+ errno = EAFNOSUPPORT;
+ return -1;
+}
+
+static int sockaddr_convert_from_un(const struct socket_info *si,
+ const struct sockaddr_un *in_addr,
+ socklen_t un_addrlen,
+ int family,
+ struct sockaddr *out_addr,
+ socklen_t *out_addrlen)
+{
+ int ret;
+
+ if (out_addr == NULL || out_addrlen == NULL)
+ return 0;
+
+ if (un_addrlen == 0) {
+ *out_addrlen = 0;
+ return 0;
+ }
+
+ switch (family) {
+ case AF_INET:
+#ifdef HAVE_IPV6
+ case AF_INET6:
+#endif
+ switch (si->type) {
+ case SOCK_STREAM:
+ case SOCK_DGRAM:
+ break;
+ default:
+ errno = ESOCKTNOSUPPORT;
+ return -1;
+ }
+ ret = convert_un_in(in_addr, out_addr, out_addrlen);
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ out_addr->sa_len = *out_addrlen;
+#endif
+ return ret;
+ default:
+ break;
+ }
+
+ errno = EAFNOSUPPORT;
+ return -1;
+}
+
+enum swrap_packet_type {
+ SWRAP_CONNECT_SEND,
+ SWRAP_CONNECT_UNREACH,
+ SWRAP_CONNECT_RECV,
+ SWRAP_CONNECT_ACK,
+ SWRAP_ACCEPT_SEND,
+ SWRAP_ACCEPT_RECV,
+ SWRAP_ACCEPT_ACK,
+ SWRAP_RECVFROM,
+ SWRAP_SENDTO,
+ SWRAP_SENDTO_UNREACH,
+ SWRAP_PENDING_RST,
+ SWRAP_RECV,
+ SWRAP_RECV_RST,
+ SWRAP_SEND,
+ SWRAP_SEND_RST,
+ SWRAP_CLOSE_SEND,
+ SWRAP_CLOSE_RECV,
+ SWRAP_CLOSE_ACK,
+};
+
+struct swrap_file_hdr {
+ uint32_t magic;
+ uint16_t version_major;
+ uint16_t version_minor;
+ int32_t timezone;
+ uint32_t sigfigs;
+ uint32_t frame_max_len;
+#define SWRAP_FRAME_LENGTH_MAX 0xFFFF
+ uint32_t link_type;
+};
+#define SWRAP_FILE_HDR_SIZE 24
+
+struct swrap_packet_frame {
+ uint32_t seconds;
+ uint32_t micro_seconds;
+ uint32_t recorded_length;
+ uint32_t full_length;
+};
+#define SWRAP_PACKET_FRAME_SIZE 16
+
+union swrap_packet_ip {
+ struct {
+ uint8_t ver_hdrlen;
+ uint8_t tos;
+ uint16_t packet_length;
+ uint16_t identification;
+ uint8_t flags;
+ uint8_t fragment;
+ uint8_t ttl;
+ uint8_t protocol;
+ uint16_t hdr_checksum;
+ uint32_t src_addr;
+ uint32_t dest_addr;
+ } v4;
+#define SWRAP_PACKET_IP_V4_SIZE 20
+ struct {
+ uint8_t ver_prio;
+ uint8_t flow_label_high;
+ uint16_t flow_label_low;
+ uint16_t payload_length;
+ uint8_t next_header;
+ uint8_t hop_limit;
+ uint8_t src_addr[16];
+ uint8_t dest_addr[16];
+ } v6;
+#define SWRAP_PACKET_IP_V6_SIZE 40
+};
+#define SWRAP_PACKET_IP_SIZE 40
+
+union swrap_packet_payload {
+ struct {
+ uint16_t source_port;
+ uint16_t dest_port;
+ uint32_t seq_num;
+ uint32_t ack_num;
+ uint8_t hdr_length;
+ uint8_t control;
+ uint16_t window;
+ uint16_t checksum;
+ uint16_t urg;
+ } tcp;
+#define SWRAP_PACKET_PAYLOAD_TCP_SIZE 20
+ struct {
+ uint16_t source_port;
+ uint16_t dest_port;
+ uint16_t length;
+ uint16_t checksum;
+ } udp;
+#define SWRAP_PACKET_PAYLOAD_UDP_SIZE 8
+ struct {
+ uint8_t type;
+ uint8_t code;
+ uint16_t checksum;
+ uint32_t unused;
+ } icmp4;
+#define SWRAP_PACKET_PAYLOAD_ICMP4_SIZE 8
+ struct {
+ uint8_t type;
+ uint8_t code;
+ uint16_t checksum;
+ uint32_t unused;
+ } icmp6;
+#define SWRAP_PACKET_PAYLOAD_ICMP6_SIZE 8
+};
+#define SWRAP_PACKET_PAYLOAD_SIZE 20
+
+#define SWRAP_PACKET_MIN_ALLOC \
+ (SWRAP_PACKET_FRAME_SIZE + \
+ SWRAP_PACKET_IP_SIZE + \
+ SWRAP_PACKET_PAYLOAD_SIZE)
+
+static const char *socket_wrapper_pcap_file(void)
+{
+ static int initialized = 0;
+ static const char *s = NULL;
+ static const struct swrap_file_hdr h;
+ static const struct swrap_packet_frame f;
+ static const union swrap_packet_ip i;
+ static const union swrap_packet_payload p;
+
+ if (initialized == 1) {
+ return s;
+ }
+ initialized = 1;
+
+ /*
+ * TODO: don't use the structs use plain buffer offsets
+ * and PUSH_U8(), PUSH_U16() and PUSH_U32()
+ *
+ * for now make sure we disable PCAP support
+ * if the struct has alignment!
+ */
+ if (sizeof(h) != SWRAP_FILE_HDR_SIZE) {
+ return NULL;
+ }
+ if (sizeof(f) != SWRAP_PACKET_FRAME_SIZE) {
+ return NULL;
+ }
+ if (sizeof(i) != SWRAP_PACKET_IP_SIZE) {
+ return NULL;
+ }
+ if (sizeof(i.v4) != SWRAP_PACKET_IP_V4_SIZE) {
+ return NULL;
+ }
+ if (sizeof(i.v6) != SWRAP_PACKET_IP_V6_SIZE) {
+ return NULL;
+ }
+ if (sizeof(p) != SWRAP_PACKET_PAYLOAD_SIZE) {
+ return NULL;
+ }
+ if (sizeof(p.tcp) != SWRAP_PACKET_PAYLOAD_TCP_SIZE) {
+ return NULL;
+ }
+ if (sizeof(p.udp) != SWRAP_PACKET_PAYLOAD_UDP_SIZE) {
+ return NULL;
+ }
+ if (sizeof(p.icmp4) != SWRAP_PACKET_PAYLOAD_ICMP4_SIZE) {
+ return NULL;
+ }
+ if (sizeof(p.icmp6) != SWRAP_PACKET_PAYLOAD_ICMP6_SIZE) {
+ return NULL;
+ }
+
+ s = getenv("SOCKET_WRAPPER_PCAP_FILE");
+ if (s == NULL) {
+ return NULL;
+ }
+ if (strncmp(s, "./", 2) == 0) {
+ s += 2;
+ }
+ return s;
+}
+
+static uint8_t *swrap_packet_init(struct timeval *tval,
+ const struct sockaddr *src,
+ const struct sockaddr *dest,
+ int socket_type,
+ const uint8_t *payload,
+ size_t payload_len,
+ unsigned long tcp_seqno,
+ unsigned long tcp_ack,
+ unsigned char tcp_ctl,
+ int unreachable,
+ size_t *_packet_len)
+{
+ uint8_t *base;
+ uint8_t *buf;
+ struct swrap_packet_frame *frame;
+ union swrap_packet_ip *ip;
+ union swrap_packet_payload *pay;
+ size_t packet_len;
+ size_t alloc_len;
+ size_t nonwire_len = sizeof(*frame);
+ size_t wire_hdr_len = 0;
+ size_t wire_len = 0;
+ size_t ip_hdr_len = 0;
+ size_t icmp_hdr_len = 0;
+ size_t icmp_truncate_len = 0;
+ uint8_t protocol = 0, icmp_protocol = 0;
+ const struct sockaddr_in *src_in = NULL;
+ const struct sockaddr_in *dest_in = NULL;
+#ifdef HAVE_IPV6
+ const struct sockaddr_in6 *src_in6 = NULL;
+ const struct sockaddr_in6 *dest_in6 = NULL;
+#endif
+ uint16_t src_port;
+ uint16_t dest_port;
+
+ switch (src->sa_family) {
+ case AF_INET:
+ src_in = (const struct sockaddr_in *)src;
+ dest_in = (const struct sockaddr_in *)dest;
+ src_port = src_in->sin_port;
+ dest_port = dest_in->sin_port;
+ ip_hdr_len = sizeof(ip->v4);
+ break;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ src_in6 = (const struct sockaddr_in6 *)src;
+ dest_in6 = (const struct sockaddr_in6 *)dest;
+ src_port = src_in6->sin6_port;
+ dest_port = dest_in6->sin6_port;
+ ip_hdr_len = sizeof(ip->v6);
+ break;
+#endif
+ default:
+ return NULL;
+ }
+
+ switch (socket_type) {
+ case SOCK_STREAM:
+ protocol = 0x06; /* TCP */
+ wire_hdr_len = ip_hdr_len + sizeof(pay->tcp);
+ wire_len = wire_hdr_len + payload_len;
+ break;
+
+ case SOCK_DGRAM:
+ protocol = 0x11; /* UDP */
+ wire_hdr_len = ip_hdr_len + sizeof(pay->udp);
+ wire_len = wire_hdr_len + payload_len;
+ break;
+
+ default:
+ return NULL;
+ }
+
+ if (unreachable) {
+ icmp_protocol = protocol;
+ switch (src->sa_family) {
+ case AF_INET:
+ protocol = 0x01; /* ICMPv4 */
+ icmp_hdr_len = ip_hdr_len + sizeof(pay->icmp4);
+ break;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ protocol = 0x3A; /* ICMPv6 */
+ icmp_hdr_len = ip_hdr_len + sizeof(pay->icmp6);
+ break;
+#endif
+ }
+ if (wire_len > 64 ) {
+ icmp_truncate_len = wire_len - 64;
+ }
+ wire_hdr_len += icmp_hdr_len;
+ wire_len += icmp_hdr_len;
+ }
+
+ packet_len = nonwire_len + wire_len;
+ alloc_len = packet_len;
+ if (alloc_len < SWRAP_PACKET_MIN_ALLOC) {
+ alloc_len = SWRAP_PACKET_MIN_ALLOC;
+ }
+
+ base = (uint8_t *)malloc(alloc_len);
+ if (!base) return NULL;
+
+ buf = base;
+
+ frame = (struct swrap_packet_frame *)buf;
+ frame->seconds = tval->tv_sec;
+ frame->micro_seconds = tval->tv_usec;
+ frame->recorded_length = wire_len - icmp_truncate_len;
+ frame->full_length = wire_len - icmp_truncate_len;
+ buf += SWRAP_PACKET_FRAME_SIZE;
+
+ ip = (union swrap_packet_ip *)buf;
+ switch (src->sa_family) {
+ case AF_INET:
+ ip->v4.ver_hdrlen = 0x45; /* version 4 and 5 * 32 bit words */
+ ip->v4.tos = 0x00;
+ ip->v4.packet_length = htons(wire_len - icmp_truncate_len);
+ ip->v4.identification = htons(0xFFFF);
+ ip->v4.flags = 0x40; /* BIT 1 set - means don't fraqment */
+ ip->v4.fragment = htons(0x0000);
+ ip->v4.ttl = 0xFF;
+ ip->v4.protocol = protocol;
+ ip->v4.hdr_checksum = htons(0x0000);
+ ip->v4.src_addr = src_in->sin_addr.s_addr;
+ ip->v4.dest_addr = dest_in->sin_addr.s_addr;
+ buf += SWRAP_PACKET_IP_V4_SIZE;
+ break;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ ip->v6.ver_prio = 0x60; /* version 4 and 5 * 32 bit words */
+ ip->v6.flow_label_high = 0x00;
+ ip->v6.flow_label_low = 0x0000;
+ ip->v6.payload_length = htons(wire_len - icmp_truncate_len); /* TODO */
+ ip->v6.next_header = protocol;
+ memcpy(ip->v6.src_addr, src_in6->sin6_addr.s6_addr, 16);
+ memcpy(ip->v6.dest_addr, dest_in6->sin6_addr.s6_addr, 16);
+ buf += SWRAP_PACKET_IP_V6_SIZE;
+ break;
+#endif
+ }
+
+ if (unreachable) {
+ pay = (union swrap_packet_payload *)buf;
+ switch (src->sa_family) {
+ case AF_INET:
+ pay->icmp4.type = 0x03; /* destination unreachable */
+ pay->icmp4.code = 0x01; /* host unreachable */
+ pay->icmp4.checksum = htons(0x0000);
+ pay->icmp4.unused = htonl(0x00000000);
+ buf += SWRAP_PACKET_PAYLOAD_ICMP4_SIZE;
+
+ /* set the ip header in the ICMP payload */
+ ip = (union swrap_packet_ip *)buf;
+ ip->v4.ver_hdrlen = 0x45; /* version 4 and 5 * 32 bit words */
+ ip->v4.tos = 0x00;
+ ip->v4.packet_length = htons(wire_len - icmp_hdr_len);
+ ip->v4.identification = htons(0xFFFF);
+ ip->v4.flags = 0x40; /* BIT 1 set - means don't fraqment */
+ ip->v4.fragment = htons(0x0000);
+ ip->v4.ttl = 0xFF;
+ ip->v4.protocol = icmp_protocol;
+ ip->v4.hdr_checksum = htons(0x0000);
+ ip->v4.src_addr = dest_in->sin_addr.s_addr;
+ ip->v4.dest_addr = src_in->sin_addr.s_addr;
+ buf += SWRAP_PACKET_IP_V4_SIZE;
+
+ src_port = dest_in->sin_port;
+ dest_port = src_in->sin_port;
+ break;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ pay->icmp6.type = 0x01; /* destination unreachable */
+ pay->icmp6.code = 0x03; /* address unreachable */
+ pay->icmp6.checksum = htons(0x0000);
+ pay->icmp6.unused = htonl(0x00000000);
+ buf += SWRAP_PACKET_PAYLOAD_ICMP6_SIZE;
+
+ /* set the ip header in the ICMP payload */
+ ip = (union swrap_packet_ip *)buf;
+ ip->v6.ver_prio = 0x60; /* version 4 and 5 * 32 bit words */
+ ip->v6.flow_label_high = 0x00;
+ ip->v6.flow_label_low = 0x0000;
+ ip->v6.payload_length = htons(wire_len - icmp_truncate_len); /* TODO */
+ ip->v6.next_header = protocol;
+ memcpy(ip->v6.src_addr, dest_in6->sin6_addr.s6_addr, 16);
+ memcpy(ip->v6.dest_addr, src_in6->sin6_addr.s6_addr, 16);
+ buf += SWRAP_PACKET_IP_V6_SIZE;
+
+ src_port = dest_in6->sin6_port;
+ dest_port = src_in6->sin6_port;
+ break;
+#endif
+ }
+ }
+
+ pay = (union swrap_packet_payload *)buf;
+
+ switch (socket_type) {
+ case SOCK_STREAM:
+ pay->tcp.source_port = src_port;
+ pay->tcp.dest_port = dest_port;
+ pay->tcp.seq_num = htonl(tcp_seqno);
+ pay->tcp.ack_num = htonl(tcp_ack);
+ pay->tcp.hdr_length = 0x50; /* 5 * 32 bit words */
+ pay->tcp.control = tcp_ctl;
+ pay->tcp.window = htons(0x7FFF);
+ pay->tcp.checksum = htons(0x0000);
+ pay->tcp.urg = htons(0x0000);
+ buf += SWRAP_PACKET_PAYLOAD_TCP_SIZE;
+
+ break;
+
+ case SOCK_DGRAM:
+ pay->udp.source_port = src_port;
+ pay->udp.dest_port = dest_port;
+ pay->udp.length = htons(8 + payload_len);
+ pay->udp.checksum = htons(0x0000);
+ buf += SWRAP_PACKET_PAYLOAD_UDP_SIZE;
+
+ break;
+ }
+
+ if (payload && payload_len > 0) {
+ memcpy(buf, payload, payload_len);
+ }
+
+ *_packet_len = packet_len - icmp_truncate_len;
+ return base;
+}
+
+static int swrap_get_pcap_fd(const char *fname)
+{
+ static int fd = -1;
+
+ if (fd != -1) return fd;
+
+ fd = open(fname, O_WRONLY|O_CREAT|O_EXCL|O_APPEND, 0644);
+ if (fd != -1) {
+ struct swrap_file_hdr file_hdr;
+ file_hdr.magic = 0xA1B2C3D4;
+ file_hdr.version_major = 0x0002;
+ file_hdr.version_minor = 0x0004;
+ file_hdr.timezone = 0x00000000;
+ file_hdr.sigfigs = 0x00000000;
+ file_hdr.frame_max_len = SWRAP_FRAME_LENGTH_MAX;
+ file_hdr.link_type = 0x0065; /* 101 RAW IP */
+
+ if (write(fd, &file_hdr, sizeof(file_hdr)) != sizeof(file_hdr)) {
+ close(fd);
+ fd = -1;
+ }
+ return fd;
+ }
+
+ fd = open(fname, O_WRONLY|O_APPEND, 0644);
+
+ return fd;
+}
+
+static uint8_t *swrap_marshall_packet(struct socket_info *si,
+ const struct sockaddr *addr,
+ enum swrap_packet_type type,
+ const void *buf, size_t len,
+ size_t *packet_len)
+{
+ const struct sockaddr *src_addr;
+ const struct sockaddr *dest_addr;
+ unsigned long tcp_seqno = 0;
+ unsigned long tcp_ack = 0;
+ unsigned char tcp_ctl = 0;
+ int unreachable = 0;
+
+ struct timeval tv;
+
+ switch (si->family) {
+ case AF_INET:
+ break;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ break;
+#endif
+ default:
+ return NULL;
+ }
+
+ switch (type) {
+ case SWRAP_CONNECT_SEND:
+ if (si->type != SOCK_STREAM) return NULL;
+
+ src_addr = si->myname;
+ dest_addr = addr;
+
+ tcp_seqno = si->io.pck_snd;
+ tcp_ack = si->io.pck_rcv;
+ tcp_ctl = 0x02; /* SYN */
+
+ si->io.pck_snd += 1;
+
+ break;
+
+ case SWRAP_CONNECT_RECV:
+ if (si->type != SOCK_STREAM) return NULL;
+
+ dest_addr = si->myname;
+ src_addr = addr;
+
+ tcp_seqno = si->io.pck_rcv;
+ tcp_ack = si->io.pck_snd;
+ tcp_ctl = 0x12; /** SYN,ACK */
+
+ si->io.pck_rcv += 1;
+
+ break;
+
+ case SWRAP_CONNECT_UNREACH:
+ if (si->type != SOCK_STREAM) return NULL;
+
+ dest_addr = si->myname;
+ src_addr = addr;
+
+ /* Unreachable: resend the data of SWRAP_CONNECT_SEND */
+ tcp_seqno = si->io.pck_snd - 1;
+ tcp_ack = si->io.pck_rcv;
+ tcp_ctl = 0x02; /* SYN */
+ unreachable = 1;
+
+ break;
+
+ case SWRAP_CONNECT_ACK:
+ if (si->type != SOCK_STREAM) return NULL;
+
+ src_addr = si->myname;
+ dest_addr = addr;
+
+ tcp_seqno = si->io.pck_snd;
+ tcp_ack = si->io.pck_rcv;
+ tcp_ctl = 0x10; /* ACK */
+
+ break;
+
+ case SWRAP_ACCEPT_SEND:
+ if (si->type != SOCK_STREAM) return NULL;
+
+ dest_addr = si->myname;
+ src_addr = addr;
+
+ tcp_seqno = si->io.pck_rcv;
+ tcp_ack = si->io.pck_snd;
+ tcp_ctl = 0x02; /* SYN */
+
+ si->io.pck_rcv += 1;
+
+ break;
+
+ case SWRAP_ACCEPT_RECV:
+ if (si->type != SOCK_STREAM) return NULL;
+
+ src_addr = si->myname;
+ dest_addr = addr;
+
+ tcp_seqno = si->io.pck_snd;
+ tcp_ack = si->io.pck_rcv;
+ tcp_ctl = 0x12; /* SYN,ACK */
+
+ si->io.pck_snd += 1;
+
+ break;
+
+ case SWRAP_ACCEPT_ACK:
+ if (si->type != SOCK_STREAM) return NULL;
+
+ dest_addr = si->myname;
+ src_addr = addr;
+
+ tcp_seqno = si->io.pck_rcv;
+ tcp_ack = si->io.pck_snd;
+ tcp_ctl = 0x10; /* ACK */
+
+ break;
+
+ case SWRAP_SEND:
+ src_addr = si->myname;
+ dest_addr = si->peername;
+
+ tcp_seqno = si->io.pck_snd;
+ tcp_ack = si->io.pck_rcv;
+ tcp_ctl = 0x18; /* PSH,ACK */
+
+ si->io.pck_snd += len;
+
+ break;
+
+ case SWRAP_SEND_RST:
+ dest_addr = si->myname;
+ src_addr = si->peername;
+
+ if (si->type == SOCK_DGRAM) {
+ return swrap_marshall_packet(si, si->peername,
+ SWRAP_SENDTO_UNREACH,
+ buf, len, packet_len);
+ }
+
+ tcp_seqno = si->io.pck_rcv;
+ tcp_ack = si->io.pck_snd;
+ tcp_ctl = 0x14; /** RST,ACK */
+
+ break;
+
+ case SWRAP_PENDING_RST:
+ dest_addr = si->myname;
+ src_addr = si->peername;
+
+ if (si->type == SOCK_DGRAM) {
+ return NULL;
+ }
+
+ tcp_seqno = si->io.pck_rcv;
+ tcp_ack = si->io.pck_snd;
+ tcp_ctl = 0x14; /* RST,ACK */
+
+ break;
+
+ case SWRAP_RECV:
+ dest_addr = si->myname;
+ src_addr = si->peername;
+
+ tcp_seqno = si->io.pck_rcv;
+ tcp_ack = si->io.pck_snd;
+ tcp_ctl = 0x18; /* PSH,ACK */
+
+ si->io.pck_rcv += len;
+
+ break;
+
+ case SWRAP_RECV_RST:
+ dest_addr = si->myname;
+ src_addr = si->peername;
+
+ if (si->type == SOCK_DGRAM) {
+ return NULL;
+ }
+
+ tcp_seqno = si->io.pck_rcv;
+ tcp_ack = si->io.pck_snd;
+ tcp_ctl = 0x14; /* RST,ACK */
+
+ break;
+
+ case SWRAP_SENDTO:
+ src_addr = si->myname;
+ dest_addr = addr;
+
+ si->io.pck_snd += len;
+
+ break;
+
+ case SWRAP_SENDTO_UNREACH:
+ dest_addr = si->myname;
+ src_addr = addr;
+
+ unreachable = 1;
+
+ break;
+
+ case SWRAP_RECVFROM:
+ dest_addr = si->myname;
+ src_addr = addr;
+
+ si->io.pck_rcv += len;
+
+ break;
+
+ case SWRAP_CLOSE_SEND:
+ if (si->type != SOCK_STREAM) return NULL;
+
+ src_addr = si->myname;
+ dest_addr = si->peername;
+
+ tcp_seqno = si->io.pck_snd;
+ tcp_ack = si->io.pck_rcv;
+ tcp_ctl = 0x11; /* FIN, ACK */
+
+ si->io.pck_snd += 1;
+
+ break;
+
+ case SWRAP_CLOSE_RECV:
+ if (si->type != SOCK_STREAM) return NULL;
+
+ dest_addr = si->myname;
+ src_addr = si->peername;
+
+ tcp_seqno = si->io.pck_rcv;
+ tcp_ack = si->io.pck_snd;
+ tcp_ctl = 0x11; /* FIN,ACK */
+
+ si->io.pck_rcv += 1;
+
+ break;
+
+ case SWRAP_CLOSE_ACK:
+ if (si->type != SOCK_STREAM) return NULL;
+
+ src_addr = si->myname;
+ dest_addr = si->peername;
+
+ tcp_seqno = si->io.pck_snd;
+ tcp_ack = si->io.pck_rcv;
+ tcp_ctl = 0x10; /* ACK */
+
+ break;
+ default:
+ return NULL;
+ }
+
+ swrapGetTimeOfDay(&tv);
+
+ return swrap_packet_init(&tv, src_addr, dest_addr, si->type,
+ (const uint8_t *)buf, len,
+ tcp_seqno, tcp_ack, tcp_ctl, unreachable,
+ packet_len);
+}
+
+static void swrap_dump_packet(struct socket_info *si,
+ const struct sockaddr *addr,
+ enum swrap_packet_type type,
+ const void *buf, size_t len)
+{
+ const char *file_name;
+ uint8_t *packet;
+ size_t packet_len = 0;
+ int fd;
+
+ file_name = socket_wrapper_pcap_file();
+ if (!file_name) {
+ return;
+ }
+
+ packet = swrap_marshall_packet(si, addr, type, buf, len, &packet_len);
+ if (!packet) {
+ return;
+ }
+
+ fd = swrap_get_pcap_fd(file_name);
+ if (fd != -1) {
+ if (write(fd, packet, packet_len) != packet_len) {
+ free(packet);
+ return;
+ }
+ }
+
+ free(packet);
+}
+
+_PUBLIC_ int swrap_socket(int family, int type, int protocol)
+{
+ struct socket_info *si;
+ struct socket_info_fd *fi;
+ int fd;
+ int real_type = type;
+#ifdef SOCK_CLOEXEC
+ real_type &= ~SOCK_CLOEXEC;
+#endif
+#ifdef SOCK_NONBLOCK
+ real_type &= ~SOCK_NONBLOCK;
+#endif
+
+ if (!socket_wrapper_dir()) {
+ return real_socket(family, type, protocol);
+ }
+
+ switch (family) {
+ case AF_INET:
+#ifdef HAVE_IPV6
+ case AF_INET6:
+#endif
+ break;
+ case AF_UNIX:
+ return real_socket(family, type, protocol);
+ default:
+ errno = EAFNOSUPPORT;
+ return -1;
+ }
+
+ switch (real_type) {
+ case SOCK_STREAM:
+ break;
+ case SOCK_DGRAM:
+ break;
+ default:
+ errno = EPROTONOSUPPORT;
+ return -1;
+ }
+
+ switch (protocol) {
+ case 0:
+ break;
+ case 6:
+ if (real_type == SOCK_STREAM) {
+ break;
+ }
+ /*fall through*/
+ case 17:
+ if (real_type == SOCK_DGRAM) {
+ break;
+ }
+ /*fall through*/
+ default:
+ errno = EPROTONOSUPPORT;
+ return -1;
+ }
+
+ /* We must call real_socket with type, from the caller, not the version we removed
+ SOCK_CLOEXEC and SOCK_NONBLOCK from */
+ fd = real_socket(AF_UNIX, type, 0);
+
+ if (fd == -1) return -1;
+
+ si = (struct socket_info *)calloc(1, sizeof(struct socket_info));
+ if (si == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ si->family = family;
+
+ /* however, the rest of the socket_wrapper code expects just
+ * the type, not the flags */
+ si->type = real_type;
+ si->protocol = protocol;
+
+ fi = (struct socket_info_fd *)calloc(1, sizeof(struct socket_info_fd));
+ if (fi == NULL) {
+ free(si);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fi->fd = fd;
+
+ SWRAP_DLIST_ADD(si->fds, fi);
+ SWRAP_DLIST_ADD(sockets, si);
+
+ return fd;
+}
+
+_PUBLIC_ int swrap_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
+{
+ struct socket_info *parent_si, *child_si;
+ struct socket_info_fd *child_fi;
+ int fd;
+ struct sockaddr_un un_addr;
+ socklen_t un_addrlen = sizeof(un_addr);
+ struct sockaddr_un un_my_addr;
+ socklen_t un_my_addrlen = sizeof(un_my_addr);
+ struct sockaddr *my_addr;
+ socklen_t my_addrlen, len;
+ int ret;
+
+ parent_si = find_socket_info(s);
+ if (!parent_si) {
+ return real_accept(s, addr, addrlen);
+ }
+
+ /*
+ * assume out sockaddr have the same size as the in parent
+ * socket family
+ */
+ my_addrlen = socket_length(parent_si->family);
+ if (my_addrlen <= 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ my_addr = (struct sockaddr *)malloc(my_addrlen);
+ if (my_addr == NULL) {
+ return -1;
+ }
+
+ memset(&un_addr, 0, sizeof(un_addr));
+ memset(&un_my_addr, 0, sizeof(un_my_addr));
+
+ ret = real_accept(s, (struct sockaddr *)(void *)&un_addr, &un_addrlen);
+ if (ret == -1) {
+ free(my_addr);
+ return ret;
+ }
+
+ fd = ret;
+
+ len = my_addrlen;
+ ret = sockaddr_convert_from_un(parent_si, &un_addr, un_addrlen,
+ parent_si->family, my_addr, &len);
+ if (ret == -1) {
+ free(my_addr);
+ close(fd);
+ return ret;
+ }
+
+ child_si = (struct socket_info *)malloc(sizeof(struct socket_info));
+ memset(child_si, 0, sizeof(*child_si));
+
+ child_fi = (struct socket_info_fd *)calloc(1, sizeof(struct socket_info_fd));
+ if (child_fi == NULL) {
+ free(child_si);
+ free(my_addr);
+ close(fd);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ child_fi->fd = fd;
+
+ SWRAP_DLIST_ADD(child_si->fds, child_fi);
+
+ child_si->family = parent_si->family;
+ child_si->type = parent_si->type;
+ child_si->protocol = parent_si->protocol;
+ child_si->bound = 1;
+ child_si->is_server = 1;
+ child_si->connected = 1;
+
+ child_si->peername_len = len;
+ child_si->peername = sockaddr_dup(my_addr, len);
+
+ if (addr != NULL && addrlen != NULL) {
+ size_t copy_len = MIN(*addrlen, len);
+ if (copy_len > 0) {
+ memcpy(addr, my_addr, copy_len);
+ }
+ *addrlen = len;
+ }
+
+ ret = real_getsockname(fd, (struct sockaddr *)(void *)&un_my_addr,
+ &un_my_addrlen);
+ if (ret == -1) {
+ free(child_fi);
+ free(child_si);
+ close(fd);
+ return ret;
+ }
+
+ len = my_addrlen;
+ ret = sockaddr_convert_from_un(child_si, &un_my_addr, un_my_addrlen,
+ child_si->family, my_addr, &len);
+ if (ret == -1) {
+ free(child_fi);
+ free(child_si);
+ free(my_addr);
+ close(fd);
+ return ret;
+ }
+
+ child_si->myname_len = len;
+ child_si->myname = sockaddr_dup(my_addr, len);
+ free(my_addr);
+
+ SWRAP_DLIST_ADD(sockets, child_si);
+
+ swrap_dump_packet(child_si, addr, SWRAP_ACCEPT_SEND, NULL, 0);
+ swrap_dump_packet(child_si, addr, SWRAP_ACCEPT_RECV, NULL, 0);
+ swrap_dump_packet(child_si, addr, SWRAP_ACCEPT_ACK, NULL, 0);
+
+ return fd;
+}
+
+static int autobind_start_init;
+static int autobind_start;
+
+/* using sendto() or connect() on an unbound socket would give the
+ recipient no way to reply, as unlike UDP and TCP, a unix domain
+ socket can't auto-assign emphemeral port numbers, so we need to
+ assign it here.
+ Note: this might change the family from ipv6 to ipv4
+*/
+static int swrap_auto_bind(int fd, struct socket_info *si, int family)
+{
+ struct sockaddr_un un_addr;
+ int i;
+ char type;
+ int ret;
+ int port;
+ struct stat st;
+
+ if (autobind_start_init != 1) {
+ autobind_start_init = 1;
+ autobind_start = getpid();
+ autobind_start %= 50000;
+ autobind_start += 10000;
+ }
+
+ un_addr.sun_family = AF_UNIX;
+
+ switch (family) {
+ case AF_INET: {
+ struct sockaddr_in in;
+
+ switch (si->type) {
+ case SOCK_STREAM:
+ type = SOCKET_TYPE_CHAR_TCP;
+ break;
+ case SOCK_DGRAM:
+ type = SOCKET_TYPE_CHAR_UDP;
+ break;
+ default:
+ errno = ESOCKTNOSUPPORT;
+ return -1;
+ }
+
+ memset(&in, 0, sizeof(in));
+ in.sin_family = AF_INET;
+ in.sin_addr.s_addr = htonl(127<<24 |
+ socket_wrapper_default_iface());
+
+ si->myname_len = sizeof(in);
+ si->myname = sockaddr_dup(&in, si->myname_len);
+ break;
+ }
+#ifdef HAVE_IPV6
+ case AF_INET6: {
+ struct sockaddr_in6 in6;
+
+ if (si->family != family) {
+ errno = ENETUNREACH;
+ return -1;
+ }
+
+ switch (si->type) {
+ case SOCK_STREAM:
+ type = SOCKET_TYPE_CHAR_TCP_V6;
+ break;
+ case SOCK_DGRAM:
+ type = SOCKET_TYPE_CHAR_UDP_V6;
+ break;
+ default:
+ errno = ESOCKTNOSUPPORT;
+ return -1;
+ }
+
+ memset(&in6, 0, sizeof(in6));
+ in6.sin6_family = AF_INET6;
+ in6.sin6_addr = *swrap_ipv6();
+ in6.sin6_addr.s6_addr[15] = socket_wrapper_default_iface();
+ si->myname_len = sizeof(in6);
+ si->myname = sockaddr_dup(&in6, si->myname_len);
+ break;
+ }
+#endif
+ default:
+ errno = ESOCKTNOSUPPORT;
+ return -1;
+ }
+
+ if (autobind_start > 60000) {
+ autobind_start = 10000;
+ }
+
+ for (i=0;i<1000;i++) {
+ port = autobind_start + i;
+ snprintf(un_addr.sun_path, sizeof(un_addr.sun_path),
+ "%s/"SOCKET_FORMAT, socket_wrapper_dir(),
+ type, socket_wrapper_default_iface(), port);
+ if (stat(un_addr.sun_path, &st) == 0) continue;
+
+ ret = real_bind(fd, (struct sockaddr *)(void *)&un_addr,
+ sizeof(un_addr));
+ if (ret == -1) return ret;
+
+ si->tmp_path = strdup(un_addr.sun_path);
+ si->bound = 1;
+ autobind_start = port + 1;
+ break;
+ }
+ if (i == 1000) {
+ errno = ENFILE;
+ return -1;
+ }
+
+ si->family = family;
+ set_port(si->family, port, si->myname);
+
+ return 0;
+}
+
+
+_PUBLIC_ int swrap_connect(int s, const struct sockaddr *serv_addr, socklen_t addrlen)
+{
+ int ret;
+ struct sockaddr_un un_addr;
+ struct socket_info *si = find_socket_info(s);
+ int bcast = 0;
+
+ if (!si) {
+ return real_connect(s, serv_addr, addrlen);
+ }
+
+ if (si->bound == 0) {
+ ret = swrap_auto_bind(s, si, serv_addr->sa_family);
+ if (ret == -1) return -1;
+ }
+
+ if (si->family != serv_addr->sa_family) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ret = sockaddr_convert_to_un(si, serv_addr,
+ addrlen, &un_addr, 0, &bcast);
+ if (ret == -1) return -1;
+
+ if (bcast) {
+ errno = ENETUNREACH;
+ return -1;
+ }
+
+ if (si->type == SOCK_DGRAM) {
+ si->defer_connect = 1;
+ ret = 0;
+ } else {
+ swrap_dump_packet(si, serv_addr, SWRAP_CONNECT_SEND, NULL, 0);
+
+ ret = real_connect(s, (struct sockaddr *)(void *)&un_addr,
+ sizeof(struct sockaddr_un));
+ }
+
+ /* to give better errors */
+ if (ret == -1 && errno == ENOENT) {
+ errno = EHOSTUNREACH;
+ }
+
+ if (ret == 0) {
+ si->peername_len = addrlen;
+ si->peername = sockaddr_dup(serv_addr, addrlen);
+ si->connected = 1;
+
+ swrap_dump_packet(si, serv_addr, SWRAP_CONNECT_RECV, NULL, 0);
+ swrap_dump_packet(si, serv_addr, SWRAP_CONNECT_ACK, NULL, 0);
+ } else {
+ swrap_dump_packet(si, serv_addr, SWRAP_CONNECT_UNREACH, NULL, 0);
+ }
+
+ return ret;
+}
+
+_PUBLIC_ int swrap_bind(int s, const struct sockaddr *myaddr, socklen_t addrlen)
+{
+ int ret;
+ struct sockaddr_un un_addr;
+ struct socket_info *si = find_socket_info(s);
+
+ if (!si) {
+ return real_bind(s, myaddr, addrlen);
+ }
+
+ si->myname_len = addrlen;
+ si->myname = sockaddr_dup(myaddr, addrlen);
+
+ ret = sockaddr_convert_to_un(si, myaddr, addrlen, &un_addr, 1, &si->bcast);
+ if (ret == -1) return -1;
+
+ unlink(un_addr.sun_path);
+
+ ret = real_bind(s, (struct sockaddr *)(void *)&un_addr,
+ sizeof(struct sockaddr_un));
+
+ if (ret == 0) {
+ si->bound = 1;
+ }
+
+ return ret;
+}
+
+_PUBLIC_ int swrap_listen(int s, int backlog)
+{
+ int ret;
+ struct socket_info *si = find_socket_info(s);
+
+ if (!si) {
+ return real_listen(s, backlog);
+ }
+
+ ret = real_listen(s, backlog);
+
+ return ret;
+}
+
+_PUBLIC_ int swrap_getpeername(int s, struct sockaddr *name, socklen_t *addrlen)
+{
+ struct socket_info *si = find_socket_info(s);
+
+ if (!si) {
+ return real_getpeername(s, name, addrlen);
+ }
+
+ if (!si->peername)
+ {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ memcpy(name, si->peername, si->peername_len);
+ *addrlen = si->peername_len;
+
+ return 0;
+}
+
+_PUBLIC_ int swrap_getsockname(int s, struct sockaddr *name, socklen_t *addrlen)
+{
+ struct socket_info *si = find_socket_info(s);
+
+ if (!si) {
+ return real_getsockname(s, name, addrlen);
+ }
+
+ memcpy(name, si->myname, si->myname_len);
+ *addrlen = si->myname_len;
+
+ return 0;
+}
+
+_PUBLIC_ int swrap_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
+{
+ struct socket_info *si = find_socket_info(s);
+
+ if (!si) {
+ return real_getsockopt(s, level, optname, optval, optlen);
+ }
+
+ if (level == SOL_SOCKET) {
+ return real_getsockopt(s, level, optname, optval, optlen);
+ }
+
+ errno = ENOPROTOOPT;
+ return -1;
+}
+
+_PUBLIC_ int swrap_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)
+{
+ struct socket_info *si = find_socket_info(s);
+
+ if (!si) {
+ return real_setsockopt(s, level, optname, optval, optlen);
+ }
+
+ if (level == SOL_SOCKET) {
+ return real_setsockopt(s, level, optname, optval, optlen);
+ }
+
+ switch (si->family) {
+ case AF_INET:
+ return 0;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ return 0;
+#endif
+ default:
+ errno = ENOPROTOOPT;
+ return -1;
+ }
+}
+
+_PUBLIC_ int swrap_ioctl(int s, int r, void *p)
+{
+ int ret;
+ struct socket_info *si = find_socket_info(s);
+ int value;
+
+ if (!si) {
+ return real_ioctl(s, r, p);
+ }
+
+ ret = real_ioctl(s, r, p);
+
+ switch (r) {
+ case FIONREAD:
+ value = *((int *)p);
+ if (ret == -1 && errno != EAGAIN && errno != ENOBUFS) {
+ swrap_dump_packet(si, NULL, SWRAP_PENDING_RST, NULL, 0);
+ } else if (value == 0) { /* END OF FILE */
+ swrap_dump_packet(si, NULL, SWRAP_PENDING_RST, NULL, 0);
+ }
+ break;
+ }
+
+ return ret;
+}
+
+static ssize_t swrap_sendmsg_before(int fd,
+ struct socket_info *si,
+ struct msghdr *msg,
+ struct iovec *tmp_iov,
+ struct sockaddr_un *tmp_un,
+ const struct sockaddr_un **to_un,
+ const struct sockaddr **to,
+ int *bcast)
+{
+ size_t i, len = 0;
+ ssize_t ret;
+
+ if (to_un) {
+ *to_un = NULL;
+ }
+ if (to) {
+ *to = NULL;
+ }
+ if (bcast) {
+ *bcast = 0;
+ }
+
+ switch (si->type) {
+ case SOCK_STREAM:
+ if (!si->connected) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ if (msg->msg_iovlen == 0) {
+ break;
+ }
+
+ /*
+ * cut down to 1500 byte packets for stream sockets,
+ * which makes it easier to format PCAP capture files
+ * (as the caller will simply continue from here)
+ */
+
+ for (i=0; i < msg->msg_iovlen; i++) {
+ size_t nlen;
+ nlen = len + msg->msg_iov[i].iov_len;
+ if (nlen > 1500) {
+ break;
+ }
+ }
+ msg->msg_iovlen = i;
+ if (msg->msg_iovlen == 0) {
+ *tmp_iov = msg->msg_iov[0];
+ tmp_iov->iov_len = MIN(tmp_iov->iov_len, 1500);
+ msg->msg_iov = tmp_iov;
+ msg->msg_iovlen = 1;
+ }
+ break;
+
+ case SOCK_DGRAM:
+ if (si->connected) {
+ if (msg->msg_name) {
+ errno = EISCONN;
+ return -1;
+ }
+ } else {
+ const struct sockaddr *msg_name;
+ msg_name = (const struct sockaddr *)msg->msg_name;
+
+ if (msg_name == NULL) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+
+ ret = sockaddr_convert_to_un(si, msg_name, msg->msg_namelen,
+ tmp_un, 0, bcast);
+ if (ret == -1) return -1;
+
+ if (to_un) {
+ *to_un = tmp_un;
+ }
+ if (to) {
+ *to = msg_name;
+ }
+ msg->msg_name = tmp_un;
+ msg->msg_namelen = sizeof(*tmp_un);
+ }
+
+ if (si->bound == 0) {
+ ret = swrap_auto_bind(fd, si, si->family);
+ if (ret == -1) return -1;
+ }
+
+ if (!si->defer_connect) {
+ break;
+ }
+
+ ret = sockaddr_convert_to_un(si, si->peername, si->peername_len,
+ tmp_un, 0, NULL);
+ if (ret == -1) return -1;
+
+ ret = real_connect(fd, (struct sockaddr *)(void *)tmp_un,
+ sizeof(*tmp_un));
+
+ /* to give better errors */
+ if (ret == -1 && errno == ENOENT) {
+ errno = EHOSTUNREACH;
+ }
+
+ if (ret == -1) {
+ return ret;
+ }
+
+ si->defer_connect = 0;
+ break;
+ default:
+ errno = EHOSTUNREACH;
+ return -1;
+ }
+
+ return 0;
+}
+
+static void swrap_sendmsg_after(struct socket_info *si,
+ struct msghdr *msg,
+ const struct sockaddr *to,
+ ssize_t ret)
+{
+ int saved_errno = errno;
+ size_t i, len = 0;
+ uint8_t *buf;
+ off_t ofs = 0;
+ size_t avail = 0;
+ size_t remain;
+
+ /* to give better errors */
+ if (ret == -1 && saved_errno == ENOENT) {
+ saved_errno = EHOSTUNREACH;
+ }
+
+ for (i=0; i < msg->msg_iovlen; i++) {
+ avail += msg->msg_iov[i].iov_len;
+ }
+
+ if (ret == -1) {
+ remain = MIN(80, avail);
+ } else {
+ remain = ret;
+ }
+
+ /* we capture it as one single packet */
+ buf = (uint8_t *)malloc(remain);
+ if (!buf) {
+ /* we just not capture the packet */
+ errno = saved_errno;
+ return;
+ }
+
+ for (i=0; i < msg->msg_iovlen; i++) {
+ size_t this_time = MIN(remain, msg->msg_iov[i].iov_len);
+ memcpy(buf + ofs,
+ msg->msg_iov[i].iov_base,
+ this_time);
+ ofs += this_time;
+ remain -= this_time;
+ }
+ len = ofs;
+
+ switch (si->type) {
+ case SOCK_STREAM:
+ if (ret == -1) {
+ swrap_dump_packet(si, NULL, SWRAP_SEND, buf, len);
+ swrap_dump_packet(si, NULL, SWRAP_SEND_RST, NULL, 0);
+ } else {
+ swrap_dump_packet(si, NULL, SWRAP_SEND, buf, len);
+ }
+ break;
+
+ case SOCK_DGRAM:
+ if (si->connected) {
+ to = si->peername;
+ }
+ if (ret == -1) {
+ swrap_dump_packet(si, to, SWRAP_SENDTO, buf, len);
+ swrap_dump_packet(si, to, SWRAP_SENDTO_UNREACH, buf, len);
+ } else {
+ swrap_dump_packet(si, to, SWRAP_SENDTO, buf, len);
+ }
+ break;
+ }
+
+ free(buf);
+ errno = saved_errno;
+}
+
+_PUBLIC_ ssize_t swrap_recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen)
+{
+ struct sockaddr_un un_addr;
+ socklen_t un_addrlen = sizeof(un_addr);
+ int ret;
+ struct socket_info *si = find_socket_info(s);
+ struct sockaddr_storage ss;
+ socklen_t ss_len = sizeof(ss);
+
+ if (!si) {
+ return real_recvfrom(s, buf, len, flags, from, fromlen);
+ }
+
+ if (!from) {
+ from = (struct sockaddr *)(void *)&ss;
+ fromlen = &ss_len;
+ }
+
+ if (si->type == SOCK_STREAM) {
+ /* cut down to 1500 byte packets for stream sockets,
+ * which makes it easier to format PCAP capture files
+ * (as the caller will simply continue from here) */
+ len = MIN(len, 1500);
+ }
+
+ /* irix 6.4 forgets to null terminate the sun_path string :-( */
+ memset(&un_addr, 0, sizeof(un_addr));
+ ret = real_recvfrom(s, buf, len, flags,
+ (struct sockaddr *)(void *)&un_addr, &un_addrlen);
+ if (ret == -1)
+ return ret;
+
+ if (sockaddr_convert_from_un(si, &un_addr, un_addrlen,
+ si->family, from, fromlen) == -1) {
+ return -1;
+ }
+
+ swrap_dump_packet(si, from, SWRAP_RECVFROM, buf, ret);
+
+ return ret;
+}
+
+
+_PUBLIC_ ssize_t swrap_sendto(int s, const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen)
+{
+ struct msghdr msg;
+ struct iovec tmp;
+ struct sockaddr_un un_addr;
+ const struct sockaddr_un *to_un = NULL;
+ ssize_t ret;
+ struct socket_info *si = find_socket_info(s);
+ int bcast = 0;
+
+ if (!si) {
+ return real_sendto(s, buf, len, flags, to, tolen);
+ }
+
+ tmp.iov_base = discard_const_p(char, buf);
+ tmp.iov_len = len;
+
+ ZERO_STRUCT(msg);
+ msg.msg_name = discard_const_p(struct sockaddr, to); /* optional address */
+ msg.msg_namelen = tolen; /* size of address */
+ msg.msg_iov = &tmp; /* scatter/gather array */
+ msg.msg_iovlen = 1; /* # elements in msg_iov */
+#if 0 /* not available on solaris */
+ msg.msg_control = NULL; /* ancillary data, see below */
+ msg.msg_controllen = 0; /* ancillary data buffer len */
+ msg.msg_flags = 0; /* flags on received message */
+#endif
+
+ ret = swrap_sendmsg_before(s, si, &msg, &tmp, &un_addr, &to_un, &to, &bcast);
+ if (ret == -1) return -1;
+
+ buf = msg.msg_iov[0].iov_base;
+ len = msg.msg_iov[0].iov_len;
+
+ if (bcast) {
+ struct stat st;
+ unsigned int iface;
+ unsigned int prt = ntohs(((const struct sockaddr_in *)to)->sin_port);
+ char type;
+
+ type = SOCKET_TYPE_CHAR_UDP;
+
+ for(iface=0; iface <= MAX_WRAPPED_INTERFACES; iface++) {
+ snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), "%s/"SOCKET_FORMAT,
+ socket_wrapper_dir(), type, iface, prt);
+ if (stat(un_addr.sun_path, &st) != 0) continue;
+
+ /* ignore the any errors in broadcast sends */
+ real_sendto(s, buf, len, flags,
+ (struct sockaddr *)(void *)&un_addr,
+ sizeof(un_addr));
+ }
+
+ swrap_dump_packet(si, to, SWRAP_SENDTO, buf, len);
+
+ return len;
+ }
+
+ ret = real_sendto(s, buf, len, flags, (struct sockaddr *)msg.msg_name,
+ msg.msg_namelen);
+
+ swrap_sendmsg_after(si, &msg, to, ret);
+
+ return ret;
+}
+
+_PUBLIC_ ssize_t swrap_recv(int s, void *buf, size_t len, int flags)
+{
+ int ret;
+ struct socket_info *si = find_socket_info(s);
+
+ if (!si) {
+ return real_recv(s, buf, len, flags);
+ }
+
+ if (si->type == SOCK_STREAM) {
+ /* cut down to 1500 byte packets for stream sockets,
+ * which makes it easier to format PCAP capture files
+ * (as the caller will simply continue from here) */
+ len = MIN(len, 1500);
+ }
+
+ ret = real_recv(s, buf, len, flags);
+ if (ret == -1 && errno != EAGAIN && errno != ENOBUFS) {
+ swrap_dump_packet(si, NULL, SWRAP_RECV_RST, NULL, 0);
+ } else if (ret == 0) { /* END OF FILE */
+ swrap_dump_packet(si, NULL, SWRAP_RECV_RST, NULL, 0);
+ } else if (ret > 0) {
+ swrap_dump_packet(si, NULL, SWRAP_RECV, buf, ret);
+ }
+
+ return ret;
+}
+
+_PUBLIC_ ssize_t swrap_read(int s, void *buf, size_t len)
+{
+ int ret;
+ struct socket_info *si = find_socket_info(s);
+
+ if (!si) {
+ return real_read(s, buf, len);
+ }
+
+ if (si->type == SOCK_STREAM) {
+ /* cut down to 1500 byte packets for stream sockets,
+ * which makes it easier to format PCAP capture files
+ * (as the caller will simply continue from here) */
+ len = MIN(len, 1500);
+ }
+
+ ret = real_read(s, buf, len);
+ if (ret == -1 && errno != EAGAIN && errno != ENOBUFS) {
+ swrap_dump_packet(si, NULL, SWRAP_RECV_RST, NULL, 0);
+ } else if (ret == 0) { /* END OF FILE */
+ swrap_dump_packet(si, NULL, SWRAP_RECV_RST, NULL, 0);
+ } else if (ret > 0) {
+ swrap_dump_packet(si, NULL, SWRAP_RECV, buf, ret);
+ }
+
+ return ret;
+}
+
+
+_PUBLIC_ ssize_t swrap_send(int s, const void *buf, size_t len, int flags)
+{
+ struct msghdr msg;
+ struct iovec tmp;
+ struct sockaddr_un un_addr;
+ ssize_t ret;
+ struct socket_info *si = find_socket_info(s);
+
+ if (!si) {
+ return real_send(s, buf, len, flags);
+ }
+
+ tmp.iov_base = discard_const_p(char, buf);
+ tmp.iov_len = len;
+
+ ZERO_STRUCT(msg);
+ msg.msg_name = NULL; /* optional address */
+ msg.msg_namelen = 0; /* size of address */
+ msg.msg_iov = &tmp; /* scatter/gather array */
+ msg.msg_iovlen = 1; /* # elements in msg_iov */
+#if 0 /* not available on solaris */
+ msg.msg_control = NULL; /* ancillary data, see below */
+ msg.msg_controllen = 0; /* ancillary data buffer len */
+ msg.msg_flags = 0; /* flags on received message */
+#endif
+
+ ret = swrap_sendmsg_before(s, si, &msg, &tmp, &un_addr, NULL, NULL, NULL);
+ if (ret == -1) return -1;
+
+ buf = msg.msg_iov[0].iov_base;
+ len = msg.msg_iov[0].iov_len;
+
+ ret = real_send(s, buf, len, flags);
+
+ swrap_sendmsg_after(si, &msg, NULL, ret);
+
+ return ret;
+}
+
+_PUBLIC_ ssize_t swrap_sendmsg(int s, const struct msghdr *omsg, int flags)
+{
+ struct msghdr msg;
+ struct iovec tmp;
+ struct sockaddr_un un_addr;
+ const struct sockaddr_un *to_un = NULL;
+ const struct sockaddr *to = NULL;
+ ssize_t ret;
+ struct socket_info *si = find_socket_info(s);
+ int bcast = 0;
+
+ if (!si) {
+ return real_sendmsg(s, omsg, flags);
+ }
+
+ tmp.iov_base = NULL;
+ tmp.iov_len = 0;
+
+ msg = *omsg;
+#if 0
+ msg.msg_name = omsg->msg_name; /* optional address */
+ msg.msg_namelen = omsg->msg_namelen; /* size of address */
+ msg.msg_iov = omsg->msg_iov; /* scatter/gather array */
+ msg.msg_iovlen = omsg->msg_iovlen; /* # elements in msg_iov */
+ /* the following is not available on solaris */
+ msg.msg_control = omsg->msg_control; /* ancillary data, see below */
+ msg.msg_controllen = omsg->msg_controllen; /* ancillary data buffer len */
+ msg.msg_flags = omsg->msg_flags; /* flags on received message */
+#endif
+
+ ret = swrap_sendmsg_before(s, si, &msg, &tmp, &un_addr, &to_un, &to, &bcast);
+ if (ret == -1) return -1;
+
+ if (bcast) {
+ struct stat st;
+ unsigned int iface;
+ unsigned int prt = ntohs(((const struct sockaddr_in *)to)->sin_port);
+ char type;
+ size_t i, len = 0;
+ uint8_t *buf;
+ off_t ofs = 0;
+ size_t avail = 0;
+ size_t remain;
+
+ for (i=0; i < msg.msg_iovlen; i++) {
+ avail += msg.msg_iov[i].iov_len;
+ }
+
+ len = avail;
+ remain = avail;
+
+ /* we capture it as one single packet */
+ buf = (uint8_t *)malloc(remain);
+ if (!buf) {
+ return -1;
+ }
+
+ for (i=0; i < msg.msg_iovlen; i++) {
+ size_t this_time = MIN(remain, msg.msg_iov[i].iov_len);
+ memcpy(buf + ofs,
+ msg.msg_iov[i].iov_base,
+ this_time);
+ ofs += this_time;
+ remain -= this_time;
+ }
+
+ type = SOCKET_TYPE_CHAR_UDP;
+
+ for(iface=0; iface <= MAX_WRAPPED_INTERFACES; iface++) {
+ snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), "%s/"SOCKET_FORMAT,
+ socket_wrapper_dir(), type, iface, prt);
+ if (stat(un_addr.sun_path, &st) != 0) continue;
+
+ msg.msg_name = &un_addr; /* optional address */
+ msg.msg_namelen = sizeof(un_addr); /* size of address */
+
+ /* ignore the any errors in broadcast sends */
+ real_sendmsg(s, &msg, flags);
+ }
+
+ swrap_dump_packet(si, to, SWRAP_SENDTO, buf, len);
+ free(buf);
+
+ return len;
+ }
+
+ ret = real_sendmsg(s, &msg, flags);
+
+ swrap_sendmsg_after(si, &msg, to, ret);
+
+ return ret;
+}
+
+int swrap_readv(int s, const struct iovec *vector, size_t count)
+{
+ int ret;
+ struct socket_info *si = find_socket_info(s);
+ struct iovec v;
+
+ if (!si) {
+ return real_readv(s, vector, count);
+ }
+
+ if (!si->connected) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ if (si->type == SOCK_STREAM && count > 0) {
+ /* cut down to 1500 byte packets for stream sockets,
+ * which makes it easier to format PCAP capture files
+ * (as the caller will simply continue from here) */
+ size_t i, len = 0;
+
+ for (i=0; i < count; i++) {
+ size_t nlen;
+ nlen = len + vector[i].iov_len;
+ if (nlen > 1500) {
+ break;
+ }
+ }
+ count = i;
+ if (count == 0) {
+ v = vector[0];
+ v.iov_len = MIN(v.iov_len, 1500);
+ vector = &v;
+ count = 1;
+ }
+ }
+
+ ret = real_readv(s, vector, count);
+ if (ret == -1 && errno != EAGAIN && errno != ENOBUFS) {
+ swrap_dump_packet(si, NULL, SWRAP_RECV_RST, NULL, 0);
+ } else if (ret == 0) { /* END OF FILE */
+ swrap_dump_packet(si, NULL, SWRAP_RECV_RST, NULL, 0);
+ } else if (ret > 0) {
+ uint8_t *buf;
+ off_t ofs = 0;
+ size_t i;
+ size_t remain = ret;
+
+ /* we capture it as one single packet */
+ buf = (uint8_t *)malloc(ret);
+ if (!buf) {
+ /* we just not capture the packet */
+ errno = 0;
+ return ret;
+ }
+
+ for (i=0; i < count; i++) {
+ size_t this_time = MIN(remain, vector[i].iov_len);
+ memcpy(buf + ofs,
+ vector[i].iov_base,
+ this_time);
+ ofs += this_time;
+ remain -= this_time;
+ }
+
+ swrap_dump_packet(si, NULL, SWRAP_RECV, buf, ret);
+ free(buf);
+ }
+
+ return ret;
+}
+
+ssize_t swrap_writev(int s, const struct iovec *vector, size_t count)
+{
+ struct msghdr msg;
+ struct iovec tmp;
+ struct sockaddr_un un_addr;
+ ssize_t ret;
+ struct socket_info *si = find_socket_info(s);
+
+ if (!si) {
+ return real_writev(s, vector, count);
+ }
+
+ tmp.iov_base = NULL;
+ tmp.iov_len = 0;
+
+ ZERO_STRUCT(msg);
+ msg.msg_name = NULL; /* optional address */
+ msg.msg_namelen = 0; /* size of address */
+ msg.msg_iov = discard_const_p(struct iovec, vector); /* scatter/gather array */
+ msg.msg_iovlen = count; /* # elements in msg_iov */
+#if 0 /* not available on solaris */
+ msg.msg_control = NULL; /* ancillary data, see below */
+ msg.msg_controllen = 0; /* ancillary data buffer len */
+ msg.msg_flags = 0; /* flags on received message */
+#endif
+
+ ret = swrap_sendmsg_before(s, si, &msg, &tmp, &un_addr, NULL, NULL, NULL);
+ if (ret == -1) return -1;
+
+ ret = real_writev(s, msg.msg_iov, msg.msg_iovlen);
+
+ swrap_sendmsg_after(si, &msg, NULL, ret);
+
+ return ret;
+}
+
+_PUBLIC_ int swrap_close(int fd)
+{
+ struct socket_info *si = find_socket_info(fd);
+ struct socket_info_fd *fi;
+ int ret;
+
+ if (!si) {
+ return real_close(fd);
+ }
+
+ for (fi = si->fds; fi; fi = fi->next) {
+ if (fi->fd == fd) {
+ SWRAP_DLIST_REMOVE(si->fds, fi);
+ free(fi);
+ break;
+ }
+ }
+
+ if (si->fds) {
+ /* there are still references left */
+ return real_close(fd);
+ }
+
+ SWRAP_DLIST_REMOVE(sockets, si);
+
+ if (si->myname && si->peername) {
+ swrap_dump_packet(si, NULL, SWRAP_CLOSE_SEND, NULL, 0);
+ }
+
+ ret = real_close(fd);
+
+ if (si->myname && si->peername) {
+ swrap_dump_packet(si, NULL, SWRAP_CLOSE_RECV, NULL, 0);
+ swrap_dump_packet(si, NULL, SWRAP_CLOSE_ACK, NULL, 0);
+ }
+
+ if (si->myname) free(si->myname);
+ if (si->peername) free(si->peername);
+ if (si->tmp_path) {
+ unlink(si->tmp_path);
+ free(si->tmp_path);
+ }
+ free(si);
+
+ return ret;
+}
+
+_PUBLIC_ int swrap_dup(int fd)
+{
+ struct socket_info *si;
+ struct socket_info_fd *fi;
+
+ si = find_socket_info(fd);
+
+ if (!si) {
+ return real_dup(fd);
+ }
+
+ fi = (struct socket_info_fd *)calloc(1, sizeof(struct socket_info_fd));
+ if (fi == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fi->fd = real_dup(fd);
+ if (fi->fd == -1) {
+ int saved_errno = errno;
+ free(fi);
+ errno = saved_errno;
+ return -1;
+ }
+
+ SWRAP_DLIST_ADD(si->fds, fi);
+ return fi->fd;
+}
+
+_PUBLIC_ int swrap_dup2(int fd, int newfd)
+{
+ struct socket_info *si;
+ struct socket_info_fd *fi;
+
+ si = find_socket_info(fd);
+
+ if (!si) {
+ return real_dup2(fd, newfd);
+ }
+
+ if (find_socket_info(newfd)) {
+ /* dup2() does an implicit close of newfd, which we
+ * need to emulate */
+ swrap_close(newfd);
+ }
+
+ fi = (struct socket_info_fd *)calloc(1, sizeof(struct socket_info_fd));
+ if (fi == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fi->fd = real_dup2(fd, newfd);
+ if (fi->fd == -1) {
+ int saved_errno = errno;
+ free(fi);
+ errno = saved_errno;
+ return -1;
+ }
+
+ SWRAP_DLIST_ADD(si->fds, fi);
+ return fi->fd;
+}
diff --git a/src/socket_wrapper.h b/src/socket_wrapper.h
new file mode 100644
index 0000000..0b5a6f5
--- /dev/null
+++ b/src/socket_wrapper.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) Jelmer Vernooij 2005 <jelmer@samba.org>
+ * Copyright (C) Stefan Metzmacher 2006 <metze@samba.org>
+ *
+ * All rights reserved.
+ *
+ * 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. 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 AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#ifndef __SOCKET_WRAPPER_H__
+#define __SOCKET_WRAPPER_H__
+
+const char *socket_wrapper_dir(void);
+unsigned int socket_wrapper_default_iface(void);
+int swrap_socket(int family, int type, int protocol);
+int swrap_accept(int s, struct sockaddr *addr, socklen_t *addrlen);
+int swrap_connect(int s, const struct sockaddr *serv_addr, socklen_t addrlen);
+int swrap_bind(int s, const struct sockaddr *myaddr, socklen_t addrlen);
+int swrap_listen(int s, int backlog);
+int swrap_getpeername(int s, struct sockaddr *name, socklen_t *addrlen);
+int swrap_getsockname(int s, struct sockaddr *name, socklen_t *addrlen);
+int swrap_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen);
+int swrap_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen);
+ssize_t swrap_recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen);
+ssize_t swrap_sendto(int s, const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen);
+ssize_t swrap_sendmsg(int s, const struct msghdr *msg, int flags);
+ssize_t swrap_recvmsg(int s, struct msghdr *msg, int flags);
+int swrap_ioctl(int s, int req, void *ptr);
+ssize_t swrap_recv(int s, void *buf, size_t len, int flags);
+ssize_t swrap_read(int s, void *buf, size_t len);
+ssize_t swrap_send(int s, const void *buf, size_t len, int flags);
+int swrap_readv(int s, const struct iovec *vector, size_t count);
+ssize_t swrap_writev(int s, const struct iovec *vector, size_t count);
+int swrap_close(int);
+int swrap_dup(int oldfd);
+int swrap_dup2(int oldfd, int newfd);
+
+#ifdef SOCKET_WRAPPER_REPLACE
+
+#ifdef accept
+#undef accept
+#endif
+#define accept(s,addr,addrlen) swrap_accept(s,addr,addrlen)
+
+#ifdef connect
+#undef connect
+#endif
+#define connect(s,serv_addr,addrlen) swrap_connect(s,serv_addr,addrlen)
+
+#ifdef bind
+#undef bind
+#endif
+#define bind(s,myaddr,addrlen) swrap_bind(s,myaddr,addrlen)
+
+#ifdef listen
+#undef listen
+#endif
+#define listen(s,blog) swrap_listen(s,blog)
+
+#ifdef getpeername
+#undef getpeername
+#endif
+#define getpeername(s,name,addrlen) swrap_getpeername(s,name,addrlen)
+
+#ifdef getsockname
+#undef getsockname
+#endif
+#define getsockname(s,name,addrlen) swrap_getsockname(s,name,addrlen)
+
+#ifdef getsockopt
+#undef getsockopt
+#endif
+#define getsockopt(s,level,optname,optval,optlen) swrap_getsockopt(s,level,optname,optval,optlen)
+
+#ifdef setsockopt
+#undef setsockopt
+#endif
+#define setsockopt(s,level,optname,optval,optlen) swrap_setsockopt(s,level,optname,optval,optlen)
+
+#ifdef recvfrom
+#undef recvfrom
+#endif
+#define recvfrom(s,buf,len,flags,from,fromlen) swrap_recvfrom(s,buf,len,flags,from,fromlen)
+
+#ifdef sendto
+#undef sendto
+#endif
+#define sendto(s,buf,len,flags,to,tolen) swrap_sendto(s,buf,len,flags,to,tolen)
+
+#ifdef sendmsg
+#undef sendmsg
+#endif
+#define sendmsg(s,msg,flags) swrap_sendmsg(s,msg,flags)
+
+#ifdef recvmsg
+#undef recvmsg
+#endif
+#define recvmsg(s,msg,flags) swrap_recvmsg(s,msg,flags)
+
+#ifdef ioctl
+#undef ioctl
+#endif
+#define ioctl(s,req,ptr) swrap_ioctl(s,req,ptr)
+
+#ifdef recv
+#undef recv
+#endif
+#define recv(s,buf,len,flags) swrap_recv(s,buf,len,flags)
+
+#ifdef read
+#undef read
+#endif
+#define read(s,buf,len) swrap_read(s,buf,len)
+
+#ifdef send
+#undef send
+#endif
+#define send(s,buf,len,flags) swrap_send(s,buf,len,flags)
+
+#ifdef readv
+#undef readv
+#endif
+#define readv(s, vector, count) swrap_readv(s,vector, count)
+
+#ifdef writev
+#undef writev
+#endif
+#define writev(s, vector, count) swrap_writev(s,vector, count)
+
+#ifdef socket
+#undef socket
+#endif
+#define socket(domain,type,protocol) swrap_socket(domain,type,protocol)
+
+#ifdef close
+#undef close
+#endif
+#define close(s) swrap_close(s)
+
+#ifdef dup
+#undef dup
+#endif
+#define dup(s) swrap_dup(s)
+
+#ifdef dup2
+#undef dup2
+#endif
+#define dup2(s, s2) swrap_dup2(s, s2)
+
+#endif /* SOCKET_WRAPPER_REPLACE */
+#endif /* __SOCKET_WRAPPER_H__ */