BIND 10 master, updated. 67b1031ec37365288502d0894cccddec7e12657e Changelog
BIND 10 source code commits
bind10-changes at lists.isc.org
Thu Jan 26 09:32:29 UTC 2012
The branch, master has been updated
via 67b1031ec37365288502d0894cccddec7e12657e (commit)
via d81cf24b9e37773ba9a0d5061c779834ff7d62b9 (commit)
via f439a4c386df14e59ca2c234c8d82d1a7dccafca (commit)
from 023d2257c83bac2e06333f2bce2e4b9eda6abbde (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 67b1031ec37365288502d0894cccddec7e12657e
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Thu Jan 26 10:22:07 2012 +0100
Changelog
commit d81cf24b9e37773ba9a0d5061c779834ff7d62b9
Merge: 023d2257c83bac2e06333f2bce2e4b9eda6abbde f439a4c386df14e59ca2c234c8d82d1a7dccafca
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Thu Jan 26 10:18:39 2012 +0100
Merge #1561
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 6 ++++++
configure.ac | 4 ++--
src/bin/ddns/ddns.py.in | 8 ++++----
src/bin/ddns/tests/ddns_test.py | 16 ++++++++--------
src/lib/python/isc/util/Makefile.am | 2 +-
src/lib/python/isc/util/{io => cio}/Makefile.am | 4 ++--
src/lib/python/isc/util/{io => cio}/__init__.py | 0
.../python/isc/util/{io => cio}/socketsession.py | 2 +-
.../isc/util/{io => cio}/socketsession_inc.cc | 0
.../isc/util/{io => cio}/socketsession_python.cc | 4 ++--
.../isc/util/{io => cio}/socketsession_python.h | 0
.../util/{io => cio}/socketsessionforwarder_inc.cc | 0
.../{io => cio}/socketsessionforwarder_python.cc | 2 +-
.../{io => cio}/socketsessionforwarder_python.h | 0
.../util/{io => cio}/socketsessionreceiver_inc.cc | 0
.../{io => cio}/socketsessionreceiver_python.cc | 4 ++--
.../{io => cio}/socketsessionreceiver_python.h | 0
.../python/isc/util/{io => cio}/tests/Makefile.am | 0
.../util/{io => cio}/tests/socketsession_test.py | 2 +-
19 files changed, 30 insertions(+), 24 deletions(-)
rename src/lib/python/isc/util/{io => cio}/Makefile.am (93%)
rename src/lib/python/isc/util/{io => cio}/__init__.py (100%)
rename src/lib/python/isc/util/{io => cio}/socketsession.py (94%)
rename src/lib/python/isc/util/{io => cio}/socketsession_inc.cc (100%)
rename src/lib/python/isc/util/{io => cio}/socketsession_python.cc (94%)
rename src/lib/python/isc/util/{io => cio}/socketsession_python.h (100%)
rename src/lib/python/isc/util/{io => cio}/socketsessionforwarder_inc.cc (100%)
rename src/lib/python/isc/util/{io => cio}/socketsessionforwarder_python.cc (99%)
rename src/lib/python/isc/util/{io => cio}/socketsessionforwarder_python.h (100%)
rename src/lib/python/isc/util/{io => cio}/socketsessionreceiver_inc.cc (100%)
rename src/lib/python/isc/util/{io => cio}/socketsessionreceiver_python.cc (99%)
rename src/lib/python/isc/util/{io => cio}/socketsessionreceiver_python.h (100%)
rename src/lib/python/isc/util/{io => cio}/tests/Makefile.am (100%)
rename src/lib/python/isc/util/{io => cio}/tests/socketsession_test.py (99%)
-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 5e7d736..b176b5d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+366. [bug] vorner
+ Fixed problem where a directory named "io" conflicted with the python3
+ standard module "io" and caused the installation to fail. The offending
+ directory has been renamed to "cio".
+ (Trac #1561, git d81cf24b9e37773ba9a0d5061c779834ff7d62b9)
+
365. [bug] jinmei
libdatasrc: in-memory datasource incorrectly returned delegation
for DS lookups.
diff --git a/configure.ac b/configure.ac
index 5121bc2..3ce0b3f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -979,8 +979,8 @@ AC_CONFIG_FILES([Makefile
src/lib/python/isc/acl/tests/Makefile
src/lib/python/isc/util/Makefile
src/lib/python/isc/util/tests/Makefile
- src/lib/python/isc/util/io/Makefile
- src/lib/python/isc/util/io/tests/Makefile
+ src/lib/python/isc/util/cio/Makefile
+ src/lib/python/isc/util/cio/tests/Makefile
src/lib/python/isc/datasrc/Makefile
src/lib/python/isc/datasrc/tests/Makefile
src/lib/python/isc/dns/Makefile
diff --git a/src/bin/ddns/ddns.py.in b/src/bin/ddns/ddns.py.in
index 1385528..cde14c7 100755
--- a/src/bin/ddns/ddns.py.in
+++ b/src/bin/ddns/ddns.py.in
@@ -23,7 +23,7 @@ from isc.dns import *
from isc.config.ccsession import *
from isc.cc import SessionError, SessionTimeout
import isc.util.process
-import isc.util.io.socketsession
+import isc.util.cio.socketsession
import select
import errno
@@ -163,9 +163,9 @@ class DDNSServer:
fileno = sock.fileno()
logger.debug(TRACE_BASIC, DDNS_NEW_CONN, fileno,
sock.getpeername())
- receiver = isc.util.io.socketsession.SocketSessionReceiver(sock)
+ receiver = isc.util.cio.socketsession.SocketSessionReceiver(sock)
self._socksession_receivers[fileno] = (sock, receiver)
- except (socket.error, isc.util.io.socketsession.SocketSessionError) \
+ except (socket.error, isc.util.cio.socketsession.SocketSessionError) \
as e:
# These exceptions mean the connection didn't work, but we can
# continue with the rest
@@ -196,7 +196,7 @@ class DDNSServer:
(socket, receiver) = self._socksession_receivers[fileno]
try:
self.handle_request(receiver.pop())
- except isc.util.io.socketsession.SocketSessionError as se:
+ except isc.util.cio.socketsession.SocketSessionError as se:
# No matter why this failed, the connection is in unknown, possibly
# broken state. So, we close the socket and remove the receiver.
del self._socksession_receivers[fileno]
diff --git a/src/bin/ddns/tests/ddns_test.py b/src/bin/ddns/tests/ddns_test.py
index 27278b4..6adb97c 100755
--- a/src/bin/ddns/tests/ddns_test.py
+++ b/src/bin/ddns/tests/ddns_test.py
@@ -21,7 +21,7 @@ import ddns
import isc.config
import select
import errno
-import isc.util.io.socketsession
+import isc.util.cio.socketsession
import socket
import os.path
@@ -111,8 +111,8 @@ class TestDDNSServer(unittest.TestCase):
def tearDown(self):
ddns.select.select = select.select
- ddns.isc.util.io.socketsession.SocketSessionReceiver = \
- isc.util.io.socketsession.SocketSessionReceiver
+ ddns.isc.util.cio.socketsession.SocketSessionReceiver = \
+ isc.util.cio.socketsession.SocketSessionReceiver
def test_listen(self):
'''
@@ -226,7 +226,7 @@ class TestDDNSServer(unittest.TestCase):
Test that we can accept a new connection.
"""
# There's nothing before the accept
- ddns.isc.util.io.socketsession.SocketSessionReceiver = \
+ ddns.isc.util.cio.socketsession.SocketSessionReceiver = \
FakeSessionReceiver
self.assertEqual({}, self.ddns_server._socksession_receivers)
self.ddns_server.accept()
@@ -259,8 +259,8 @@ class TestDDNSServer(unittest.TestCase):
# Now make the socket receiver fail
self.ddns_server._listen_socket.accept = orig
def receiver_failure(sock):
- raise isc.util.io.socketsession.SocketSessionError('Test error')
- ddns.isc.util.io.socketsession.SocketSessionReceiver = \
+ raise isc.util.cio.socketsession.SocketSessionError('Test error')
+ ddns.isc.util.cio.socketsession.SocketSessionReceiver = \
receiver_failure
# Doesn't raise the exception
self.ddns_server.accept()
@@ -269,7 +269,7 @@ class TestDDNSServer(unittest.TestCase):
# Check we don't catch everything, so raise just an exception
def unexpected_failure(sock):
raise Exception('Test error')
- ddns.isc.util.io.socketsession.SocketSessionReceiver = \
+ ddns.isc.util.cio.socketsession.SocketSessionReceiver = \
unexpected_failure
# This one gets through
self.assertRaises(Exception, self.ddns_server.accept)
@@ -325,7 +325,7 @@ class TestDDNSServer(unittest.TestCase):
socket = FakeSocket(3)
receiver = FakeSessionReceiver(socket)
def pop():
- raise isc.util.io.socketsession.SocketSessionError('Test error')
+ raise isc.util.cio.socketsession.SocketSessionError('Test error')
receiver.pop = pop
socket.close = self.__hook
self.__hook_called = False
diff --git a/src/lib/python/isc/util/Makefile.am b/src/lib/python/isc/util/Makefile.am
index 703879a..3eaaa12 100644
--- a/src/lib/python/isc/util/Makefile.am
+++ b/src/lib/python/isc/util/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = . io tests
+SUBDIRS = . cio tests
python_PYTHON = __init__.py process.py socketserver_mixin.py file.py
diff --git a/src/lib/python/isc/util/cio/Makefile.am b/src/lib/python/isc/util/cio/Makefile.am
new file mode 100644
index 0000000..7e00768
--- /dev/null
+++ b/src/lib/python/isc/util/cio/Makefile.am
@@ -0,0 +1,41 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+python_PYTHON = __init__.py
+pythondir = $(PYTHON_SITEPKG_DIR)/isc/util/cio
+
+pyexec_LTLIBRARIES = socketsession.la
+pyexecdir = $(PYTHON_SITEPKG_DIR)/isc/util/cio
+
+socketsession_la_SOURCES = socketsession_python.cc socketsession_python.h
+socketsession_la_SOURCES += socketsessionforwarder_python.cc
+socketsession_la_SOURCES += socketsessionforwarder_python.h
+socketsession_la_SOURCES += socketsessionreceiver_python.cc
+socketsession_la_SOURCES += socketsessionreceiver_python.h
+socketsession_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
+socketsession_la_LDFLAGS = $(PYTHON_LDFLAGS)
+# Note: PYTHON_CXXFLAGS may have some -Wno... workaround, which must be
+# placed after -Wextra defined in AM_CXXFLAGS
+socketsession_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
+
+# Python prefers .so, while some OSes (specifically MacOS) use a different
+# suffix for dynamic objects. -module is necessary to work this around.
+socketsession_la_LDFLAGS += -module
+socketsession_la_LIBADD = $(top_builddir)/src/lib/util/io/libutil_io.la
+socketsession_la_LIBADD += $(PYTHON_LIB)
+
+# This is not installed, it helps locate the module during tests
+EXTRA_DIST = __init__.py socketsession.py
+
+EXTRA_DIST += socketsession_inc.cc
+EXTRA_DIST += socketsessionforwarder_inc.cc socketsessionreceiver_inc.cc
+
+CLEANFILES = __init__.pyc socketsession.pyc
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/util/cio/__init__.py b/src/lib/python/isc/util/cio/__init__.py
new file mode 100644
index 0000000..935160a
--- /dev/null
+++ b/src/lib/python/isc/util/cio/__init__.py
@@ -0,0 +1,3 @@
+"""
+Here are function and classes for forwarding socket sessions between processes.
+"""
diff --git a/src/lib/python/isc/util/cio/socketsession.py b/src/lib/python/isc/util/cio/socketsession.py
new file mode 100644
index 0000000..5017d90
--- /dev/null
+++ b/src/lib/python/isc/util/cio/socketsession.py
@@ -0,0 +1,26 @@
+# Copyright (C) 2011 Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# This file is not installed. See python/isc/log/__init__.py for the trick.
+
+import os
+import sys
+
+for base in sys.path[:]:
+ libdir = os.path.join(base, 'isc/util/cio/.libs')
+ if os.path.exists(libdir):
+ sys.path.insert(0, libdir)
+
+from socketsession import *
diff --git a/src/lib/python/isc/util/cio/socketsession_inc.cc b/src/lib/python/isc/util/cio/socketsession_inc.cc
new file mode 100644
index 0000000..e200063
--- /dev/null
+++ b/src/lib/python/isc/util/cio/socketsession_inc.cc
@@ -0,0 +1,122 @@
+namespace {
+const char* const socketsession_doc = "\
+This module defines a set of classes that support forwarding a\n\
+\"socket session\" from one process to another. A socket session is a\n\
+conceptual tuple of the following elements:\n\
+\n\
+- A network socket\n\
+- The local and remote endpoints of a (IP) communication taking place\n\
+ on the socket. In practice an endpoint is a pair of an IP address\n\
+ and TCP or UDP port number.\n\
+- Some amount of data sent from the remote endpoint and received on\n\
+ the socket. We call it (socket) session data in this documentation.\n\
+\n\
+Note that this is a conceptual definition. Depending on the underlying\n\
+implementation and/or the network protocol, some of the elements could\n\
+be part of others; for example, if it's an established TCP connection,\n\
+the local and remote endpoints would be able to be retrieved from the\n\
+socket using the standard getsockname() and getpeername() system\n\
+calls. But in this definition we separate these to be more generic.\n\
+Also, as a matter of fact our intended usage includes non-connected\n\
+UDP communications, in which case at least the remote endpoint should\n\
+be provided separately from the socket.\n\
+\n\
+In the actual implementation we represent a socket as a Python socket\n\
+object, which contains the information of the address family\n\
+(e.g. AF_INET6), socket type (e.g. SOCK_STREAM), and protocol\n\
+(e.g. IPPROTO_TCP).\n\
+\n\
+We use the Python socket address tuple to represent endpoints.\n\
+\n\
+Socket session data is an opaque blob in the form of a Python byte\n\
+object.\n\
+\n\
+To forward a socket session between processes, we use connected UNIX\n\
+domain sockets established between the processes. The file descriptor\n\
+will be forwarded through the sockets as an ancillary data item of\n\
+type SCM_RIGHTS. Other elements of the session will be transferred as\n\
+normal data over the connection.\n\
+\n\
+We provide two classes to help applications forward socket sessions:\n\
+SocketSessionForwarder is the sender of the UNIX domain connection,\n\
+while SocketSessionReceiver is the receiver (this interface assumes\n\
+one direction of forwarding).\n\
+\n\
+Note: this paragraph and following discussions on the internal\n\
+protocol are for reference purposes only; it's not necessary to\n\
+understand how to use the API.\n\
+SocketSessionForwarder and SocketSessionReceiver objects (internally)\n\
+use a straightforward protocol to pass elements of socket sessions.\n\
+Once the connection is established, the forwarder object first forwards\n\
+the file descriptor with 1-byte dummy data. It then forwards a\n\
+\"(socket) session header\", which contains all other elements of\n\
+the session except the file descriptor (already forwarded) and session\n\
+data. The wire format of the header is as follows:\n\
+\n\
+- The length of the header (16-bit unsigned integer)\n\
+- Address family\n\
+- Socket type\n\
+- Protocol\n\
+- Size of the local endpoint in bytes\n\
+- Local endpoint (a copy of the memory image of the corresponding\n\
+ sockaddr)\n\
+- Size of the remote endpoint in bytes\n\
+- Remote endpoint (same as local endpoint)\n\
+- Size of session data in bytes\n\
+\n\
+The type of the fields is 32-bit unsigned integer unless explicitly\n\
+noted, and all fields are formatted in the network byte order.\n\
+\n\
+The socket session data immediately follows the session header.\n\
+\n\
+Note that the fields do not necessarily be in the network byte order\n\
+because they are expected to be exchanged on the same machine.\n\
+Likewise, integer elements such as address family do not necessarily\n\
+be represented as an fixed-size value (i.e., 32-bit). But fixed size\n\
+fields are used in order to ensure maximum portability in such a\n\
+(rare) case where the forwarder and the receiver are built with\n\
+different compilers that have different definitions of int. Also,\n\
+since sockaddr fields are generally formatted in the network byte\n\
+order, other fields are defined so to be consistent.\n\
+\n\
+One basic assumption in the API of this module is socket sessions\n\
+should be forwarded without blocking, thus eliminating the need for\n\
+incremental read/write or blocking other important services such as\n\
+responding to requests from the application's clients. This assumption\n\
+should be held as long as both the forwarder and receiver have\n\
+sufficient resources to handle the forwarding process since the\n\
+communication is local. But a forward attempt could still block if the\n\
+receiver is busy (or even hang up) and cannot keep up with the volume\n\
+of incoming sessions.\n\
+\n\
+So, in this implementation, the forwarder uses non blocking writes to\n\
+forward sessions. If a write attempt could block, it immediately gives\n\
+up the operation with an exception. The corresponding application is\n\
+expected to catch it, close the connection, and perform any necessary\n\
+recovery for that application (that would normally be re-establish the\n\
+connection with a new receiver, possibly after confirming the\n\
+receiving side is still alive). On the other hand, the receiver\n\
+implementation assumes it's possible that it only receive incomplete\n\
+elements of a session (such as in the case where the forwarder writes\n\
+part of the entire session and gives up the connection). The receiver\n\
+implementation throws an exception when it encounters an incomplete\n\
+session. Like the case of the forwarder application, the receiver\n\
+application is expected to catch it, close the connection, and perform\n\
+any necessary recovery steps.\n\
+\n\
+Note that the receiver implementation uses blocking read. So it's\n\
+application's responsibility to ensure that there's at least some data\n\
+in the connection when the receiver object is requested to receive a\n\
+session (unless this operation can be blocking, e.g., by the use of a\n\
+separate thread). Also, if the forwarder implementation or application\n\
+is malicious or extremely buggy and intentionally sends partial\n\
+session and keeps the connection, the receiver could block in\n\
+receiving a session. In general, we assume the forwarder doesn't do\n\
+intentional blocking as it's a local node and is generally a module of\n\
+the same (BIND 10) system. The minimum requirement for the forwarder\n\
+implementation (and application) is to make sure the connection is\n\
+closed once it detects an error on it. Even a naive implementation\n\
+that simply dies due to the exception will meet this requirement.\n\
+\n\
+";
+} // unnamed namespace
diff --git a/src/lib/python/isc/util/cio/socketsession_python.cc b/src/lib/python/isc/util/cio/socketsession_python.cc
new file mode 100644
index 0000000..8fdfbc1
--- /dev/null
+++ b/src/lib/python/isc/util/cio/socketsession_python.cc
@@ -0,0 +1,79 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <Python.h>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include "socketsessionreceiver_python.h"
+#include "socketsessionforwarder_python.h"
+
+using namespace isc::util::io::python;
+using namespace isc::util::python;
+
+#include "socketsession_inc.cc"
+
+namespace isc {
+namespace util {
+namespace io {
+namespace python {
+PyObject* po_SocketSessionError;
+}
+}
+}
+}
+
+namespace {
+
+PyModuleDef socketsession = {
+ { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
+ "isc.util.cio.socketsession",
+ socketsession_doc,
+ -1,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+} // end of unnamed namespace
+
+PyMODINIT_FUNC
+PyInit_socketsession(void) {
+ PyObject* mod = PyModule_Create(&socketsession);
+ if (mod == NULL) {
+ return (NULL);
+ }
+
+ try {
+ po_SocketSessionError =
+ PyErr_NewException("isc.util.cio.SocketSessionError", NULL, NULL);
+ PyObjectContainer(po_SocketSessionError).
+ installToModule(mod, "SocketSessionError");
+ } catch (...) {
+ Py_DECREF(mod);
+ return (NULL);
+ }
+
+ if (!initModulePart_SocketSessionForwarder(mod)) {
+ Py_DECREF(mod);
+ return (NULL);
+ }
+ if (!initModulePart_SocketSessionReceiver(mod)) {
+ Py_DECREF(mod);
+ return (NULL);
+ }
+
+ return (mod);
+}
diff --git a/src/lib/python/isc/util/cio/socketsession_python.h b/src/lib/python/isc/util/cio/socketsession_python.h
new file mode 100644
index 0000000..b0703ac
--- /dev/null
+++ b/src/lib/python/isc/util/cio/socketsession_python.h
@@ -0,0 +1,35 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_SOCKETSESSION_H
+#define __PYTHON_SOCKETSESSION_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace util {
+namespace io {
+namespace python {
+
+extern PyObject* po_SocketSessionError;
+
+} // namespace python
+} // namespace io
+} // namespace util
+} // namespace isc
+#endif // __PYTHON_SOCKETSESSION_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/python/isc/util/cio/socketsessionforwarder_inc.cc b/src/lib/python/isc/util/cio/socketsessionforwarder_inc.cc
new file mode 100644
index 0000000..6b9de01
--- /dev/null
+++ b/src/lib/python/isc/util/cio/socketsessionforwarder_inc.cc
@@ -0,0 +1,136 @@
+namespace {
+// Modifications:
+// reference to the module description (instead of "utility")
+// exception description
+const char* const SocketSessionForwarder_doc = "\
+The forwarder of socket sessions.\n\
+\n\
+An object of this class maintains a UNIX domain socket (normally\n\
+expected to be connected to a SocketSessionReceiver object) and\n\
+forwards socket sessions to the receiver.\n\
+\n\
+See the description of socketsession module for other details of how\n\
+the session forwarding works.\n\
+\n\
+SocketSessionForwarder(unix_file)\n\
+\n\
+ The constructor.\n\
+\n\
+ It's constructed with path information of the intended receiver,\n\
+ but does not immediately establish a connection to the receiver;\n\
+ connect_to_receiver() must be called to establish it. These are\n\
+ separated so that an object of class can be initialized (possibly\n\
+ as an attribute of a higher level application class object)\n\
+ without knowing the receiver is ready for accepting new\n\
+ forwarders. The separate connect interface allows the object to be\n\
+ reused when it detects connection failure and tries to re-\n\
+ establish it after closing the failed one.\n\
+\n\
+ On construction, it also installs a signal filter for SIGPIPE to\n\
+ ignore it. Since this class uses a stream-type connected UNIX\n\
+ domain socket, if the receiver (abruptly) closes the connection a\n\
+ subsequent write operation on the socket would trigger a SIGPIPE\n\
+ signal, which kills the caller process by default. This behavior\n\
+ would be undesirable in many cases, so this implementation always\n\
+ disables the signal.\n\
+\n\
+ This approach has some drawbacks, however; first, since signal\n\
+ handling is process (or thread) wide, ignoring it may not what the\n\
+ application wants. On the other hand, if the application changes\n\
+ how the signal is handled after instantiating this class, the new\n\
+ behavior affects the class operation. Secondly, even if ignoring\n\
+ the signal is the desired operation, it's a waste to set the\n\
+ filter every time this class object is constructed. It's\n\
+ sufficient to do it once. We still adopt this behavior based on\n\
+ the observation that in most cases applications would like to\n\
+ ignore SIGPIPE (or simply doesn't care about it) and that this\n\
+ class is not instantiated so often (so the wasteful setting\n\
+ overhead should be marginal). On the other hand, doing it every\n\
+ time is beneficial if the application is threaded and different\n\
+ threads create different forwarder objects (and if signals work\n\
+ per thread).\n\
+\n\
+ Exceptions:\n\
+ SocketSessionError unix_file is invalid as a path name of a UNIX\n\
+ domain socket or error happens in setting a filter for\n\
+ SIGPIPE (see above)\n\
+ SystemError Unexpected errors such as resource allocation failure\n\
+\n\
+ Parameters:\n\
+ unix_file Path name of the receiver.\n\
+\n\
+";
+
+// Modifications:
+// exception description
+const char* const SocketSessionForwarder_connectToReceiver_doc = "\
+connect_to_receiver()\n\
+\n\
+Establish a connection to the receiver.\n\
+\n\
+This method establishes a connection to the receiver at the path given\n\
+on construction. It makes the underlying UNIX domain socket non\n\
+blocking, so this method (or subsequent push() calls) does not block.\n\
+\n\
+Exceptions:\n\
+ TypeError The method is called while an already established\n\
+ connection is still active.\n\
+ SocketSessionError A system error in socket operation.\n\
+ SystemError Unexpected errors such as resource allocation failure\n\
+\n\
+";
+
+// Modifications:
+// bullet description
+// parameters
+// exception description
+const char* const SocketSessionForwarder_push_doc = "\
+push(sock, family, type, protocol, local_end, remote_end, data)\n\
+\n\
+Forward a socket session to the receiver.\n\
+\n\
+This method takes a set of parameters that represent a single socket\n\
+session, renders them in the \"wire\" format according to the internal\n\
+protocol (see socketsession module) and forwards them to the\n\
+receiver through the UNIX domain connection.\n\
+\n\
+The connection must have been established by connect_to_receiver().\n\
+\n\
+For simplicity and for the convenience of detecting application\n\
+errors, this method imposes some restrictions on the parameters:\n\
+\n\
+- Socket family must be either AF_INET or AF_INET6\n\
+- The address family (sa_family) member of the local and remote end\n\
+ points must be equal to the family parameter\n\
+- Socket session data must not be empty\n\
+- Data length must not exceed 65535\n\
+\n\
+These are not architectural limitation, and might be loosened in future\n\
+versions as we see the need for flexibility.\n\
+\n\
+Since the underlying UNIX domain socket is non blocking (see the\n\
+description for the constructor), a call to this method should either\n\
+return immediately or result in exception (in case of \"would\n\
+block\").\n\
+\n\
+Exceptions:\n\
+ TypeError The method is called before establishing a connection or\n\
+ given parameters are invalid, or the given socket address\n\
+ is valid.\n\
+ SocketSessionError A system error in socket operation, including the\n\
+ case where the write operation would block.\n\
+\n\
+Parameters:\n\
+ sock (int) The socket file descriptor\n\
+ family (int) The address family (such as socket.AF_INET6) of the\n\
+ socket\n\
+ type (int) The socket type (such as socket.SOCK_DGRAM) of the\n\
+ socket\n\
+ protocol (int) The transport protocol (such as socket.IPPROTO_UDP)\n\
+ of the socket\n\
+ local_end (socket address) The local end point of the session\n\
+ remote_end (socket address) The remote end point of the session\n\
+ data (byte) the session data\n\
+\n\
+";
+} // unnamed namespace
diff --git a/src/lib/python/isc/util/cio/socketsessionforwarder_python.cc b/src/lib/python/isc/util/cio/socketsessionforwarder_python.cc
new file mode 100644
index 0000000..583a877
--- /dev/null
+++ b/src/lib/python/isc/util/cio/socketsessionforwarder_python.cc
@@ -0,0 +1,309 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// Enable this if you use s# variants with PyArg_ParseTuple(), see
+// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
+//#define PY_SSIZE_T_CLEAN
+
+// Python.h needs to be placed at the head of the program file, see:
+// http://docs.python.org/py3k/extending/extending.html#a-simple-example
+#include <Python.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include <string>
+#include <stdexcept>
+
+#include <boost/lexical_cast.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <util/io/sockaddr_util.h>
+#include <util/io/socketsession.h>
+#include <util/python/pycppwrapper_util.h>
+
+#include "socketsession_python.h"
+#include "socketsessionforwarder_python.h"
+
+using namespace std;
+using namespace isc::util::python;
+using namespace isc::util::io;
+using namespace isc::util::io::internal;
+using namespace isc::util::io::python;
+using boost::lexical_cast;
+
+// Trivial constructor.
+s_SocketSessionForwarder::s_SocketSessionForwarder() : cppobj(NULL) {
+}
+
+// Import pydoc text
+#include "socketsessionforwarder_inc.cc"
+
+// See python/isc/log/log.cc for the use of namespace
+namespace clang_unnamed_namespace_workaround {
+// Internal exception class thrown when address parsing fails
+class AddressParseError: public isc::Exception {
+public:
+ AddressParseError(const char *file, size_t line, const char *what):
+ isc::Exception(file, line, what) {}
+};
+}
+using namespace clang_unnamed_namespace_workaround;
+
+namespace {
+
+int
+SocketSessionForwarder_init(PyObject* po_self, PyObject* args, PyObject*) {
+ s_SocketSessionForwarder* self =
+ static_cast<s_SocketSessionForwarder*>(po_self);
+ try {
+ const char* unix_file;
+ if (PyArg_ParseTuple(args, "s", &unix_file)) {
+ self->cppobj = new SocketSessionForwarder(unix_file);
+ return (0);
+ }
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Failed to construct SocketSessionForwarder object: " +
+ string(ex.what());
+ PyErr_SetString(po_SocketSessionError, ex_what.c_str());
+ return (-1);
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
+ return (-1);
+ }
+
+ return (-1);
+}
+
+void
+SocketSessionForwarder_destroy(PyObject* po_self) {
+ s_SocketSessionForwarder* self =
+ static_cast<s_SocketSessionForwarder*>(po_self);
+ delete self->cppobj;
+ self->cppobj = NULL;
+ Py_TYPE(self)->tp_free(self);
+}
+
+// Convert a Python socket address object to an addrinfo structure by
+// getaddrinfo.
+void
+parsePySocketAddress(PyObject* obj, int type, int protocol,
+ struct sockaddr_storage* ss)
+{
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = type;
+ hints.ai_protocol = protocol;
+ hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
+
+ const char* addr;
+ int port, flowinfo, scopeid;
+ struct addrinfo *res;
+ if (PyArg_ParseTuple(obj, "si", &addr, &port)) {
+ // Possibly an IPv4 address.
+ hints.ai_family = AF_INET;
+ const int error = getaddrinfo(addr,
+ lexical_cast<string>(port).c_str(),
+ &hints, &res);
+ if (error == 0) {
+ assert(res->ai_addrlen <= sizeof(*ss));
+ memcpy(ss, res->ai_addr, res->ai_addrlen);
+ return;
+ }
+ isc_throw(AddressParseError, "Invalid or unsupported socket address: "
+ << gai_strerror(error));
+ }
+ PyErr_Clear();
+ if (PyArg_ParseTuple(obj, "siii", &addr, &port, &flowinfo, &scopeid)) {
+ // Possibly an IPv6 address. We ignore flowinfo.
+ hints.ai_family = AF_INET6;
+ const int error = getaddrinfo(addr,
+ lexical_cast<string>(port).c_str(),
+ &hints, &res);
+ if (error == 0) {
+ assert(res->ai_addrlen <= sizeof(*ss));
+ memcpy(ss, res->ai_addr, res->ai_addrlen);
+ void* p = ss;
+ static_cast<struct sockaddr_in6*>(p)->sin6_scope_id = scopeid;
+ return;
+ }
+ isc_throw(AddressParseError, "Invalid or unsupported socket address: "
+ << gai_strerror(error));
+ }
+ PyErr_Clear();
+ isc_throw(AddressParseError, "Invalid or unsupported socket address, must "
+ "be AF_INET or AF_INET6 socket address.");
+}
+
+PyObject*
+SocketSessionForwarder_connectToReceiver(PyObject* po_self, PyObject*) {
+ s_SocketSessionForwarder* const self =
+ static_cast<s_SocketSessionForwarder*>(po_self);
+
+ try {
+ self->cppobj->connectToReceiver();
+ Py_RETURN_NONE;
+ } catch (const isc::BadValue& ex) {
+ PyErr_SetString(PyExc_TypeError, ex.what());
+ return (NULL);
+ } catch (const SocketSessionError& ex) {
+ PyErr_SetString(po_SocketSessionError, ex.what());
+ return (NULL);
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Unexpected failure in connecting to receiver: " +
+ string(ex.what());
+ PyErr_SetString(PyExc_SystemError, ex_what.c_str());
+ return (NULL);
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
+ return (NULL);
+ }
+}
+
+PyObject*
+SocketSessionForwarder_push(PyObject* po_self, PyObject* args) {
+ s_SocketSessionForwarder* const self =
+ static_cast<s_SocketSessionForwarder*>(po_self);
+
+ try {
+ int fd, family, type, protocol;
+ PyObject* po_local_end;
+ PyObject* po_remote_end;
+ Py_buffer py_buf;
+
+ if (!PyArg_ParseTuple(args, "iiiiOOy*", &fd, &family, &type, &protocol,
+ &po_local_end, &po_remote_end, &py_buf)) {
+ return (NULL);
+ }
+ struct sockaddr_storage ss_local, ss_remote;
+ parsePySocketAddress(po_local_end, type, protocol, &ss_local);
+ parsePySocketAddress(po_remote_end, type, protocol, &ss_remote);
+ self->cppobj->push(fd, family, type, protocol,
+ *convertSockAddr(&ss_local),
+ *convertSockAddr(&ss_remote),
+ py_buf.buf, py_buf.len);
+ Py_RETURN_NONE;
+ } catch (const AddressParseError& ex) {
+ PyErr_SetString(PyExc_TypeError, ex.what());
+ return (NULL);
+ } catch (const isc::BadValue& ex) {
+ PyErr_SetString(PyExc_TypeError, ex.what());
+ return (NULL);
+ } catch (const SocketSessionError& ex) {
+ PyErr_SetString(po_SocketSessionError, ex.what());
+ return (NULL);
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
+ return (NULL);
+ }
+}
+
+// This list contains the actual set of functions we have in
+// python. Each entry has
+// 1. Python method name
+// 2. Our static function here
+// 3. Argument type
+// 4. Documentation
+PyMethodDef SocketSessionForwarder_methods[] = {
+ { "push", SocketSessionForwarder_push, METH_VARARGS,
+ SocketSessionForwarder_push_doc },
+ { "connect_to_receiver", SocketSessionForwarder_connectToReceiver,
+ METH_NOARGS, SocketSessionForwarder_connectToReceiver_doc },
+ { NULL, NULL, 0, NULL }
+};
+} // end of unnamed namespace
+
+namespace isc {
+namespace util {
+namespace io {
+namespace python {
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_SocketSessionForwarder
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject socketsessionforwarder_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "isc.util.cio.SocketSessionForwarder",
+ sizeof(s_SocketSessionForwarder), // tp_basicsize
+ 0, // tp_itemsize
+ SocketSessionForwarder_destroy, // tp_dealloc
+ NULL, // tp_print
+ NULL, // tp_getattr
+ NULL, // tp_setattr
+ NULL, // tp_reserved
+ NULL, // tp_repr
+ NULL, // tp_as_number
+ NULL, // tp_as_sequence
+ NULL, // tp_as_mapping
+ NULL, // tp_hash
+ NULL, // tp_call
+ NULL, // tp_str
+ NULL, // tp_getattro
+ NULL, // tp_setattro
+ NULL, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ SocketSessionForwarder_doc,
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ NULL, // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ SocketSessionForwarder_methods, // tp_methods
+ NULL, // tp_members
+ NULL, // tp_getset
+ NULL, // tp_base
+ NULL, // tp_dict
+ NULL, // tp_descr_get
+ NULL, // tp_descr_set
+ 0, // tp_dictoffset
+ SocketSessionForwarder_init, // tp_init
+ NULL, // tp_alloc
+ PyType_GenericNew, // tp_new
+ NULL, // tp_free
+ NULL, // tp_is_gc
+ NULL, // tp_bases
+ NULL, // tp_mro
+ NULL, // tp_cache
+ NULL, // tp_subclasses
+ NULL, // tp_weaklist
+ NULL, // tp_del
+ 0 // tp_version_tag
+};
+
+// Module Initialization, all statics are initialized here
+bool
+initModulePart_SocketSessionForwarder(PyObject* mod) {
+ // We initialize the static description object with PyType_Ready(),
+ // then add it to the module. This is not just a check! (leaving
+ // this out results in segmentation faults)
+ if (PyType_Ready(&socketsessionforwarder_type) < 0) {
+ return (false);
+ }
+ void* p = &socketsessionforwarder_type;
+ if (PyModule_AddObject(mod, "SocketSessionForwarder",
+ static_cast<PyObject*>(p)) < 0) {
+ return (false);
+ }
+ Py_INCREF(&socketsessionforwarder_type);
+
+ return (true);
+}
+} // namespace python
+} // namespace io
+} // namespace util
+} // namespace isc
diff --git a/src/lib/python/isc/util/cio/socketsessionforwarder_python.h b/src/lib/python/isc/util/cio/socketsessionforwarder_python.h
new file mode 100644
index 0000000..2ce220a
--- /dev/null
+++ b/src/lib/python/isc/util/cio/socketsessionforwarder_python.h
@@ -0,0 +1,45 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_SOCKETSESSIONFORWARDER_H
+#define __PYTHON_SOCKETSESSIONFORWARDER_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace util {
+namespace io {
+class SocketSessionForwarder;
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_SocketSessionForwarder : public PyObject {
+public:
+ s_SocketSessionForwarder();
+ SocketSessionForwarder* cppobj;
+};
+
+extern PyTypeObject socketsessionforwarder_type;
+
+bool initModulePart_SocketSessionForwarder(PyObject* mod);
+} // namespace python
+} // namespace io
+} // namespace util
+} // namespace isc
+#endif // __PYTHON_SOCKETSESSIONFORWARDER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/python/isc/util/cio/socketsessionreceiver_inc.cc b/src/lib/python/isc/util/cio/socketsessionreceiver_inc.cc
new file mode 100644
index 0000000..ed29d3e
--- /dev/null
+++ b/src/lib/python/isc/util/cio/socketsessionreceiver_inc.cc
@@ -0,0 +1,89 @@
+namespace {
+// Modifications
+// - about return value
+// - socket session "utility" => module
+const char* const SocketSessionReceiver_doc = "\
+The receiver of socket sessions.\n\
+\n\
+An object of this class holds a UNIX domain socket for an established\n\
+connection, receives socket sessions from the remote forwarder, and\n\
+provides the session to the application as a tuple of corresponding\n\
+elements.\n\
+\n\
+Note that this class is instantiated with an already connected socket;\n\
+it's not a listening socket that is accepting connection requests from\n\
+forwarders. It's application's responsibility to create the listening\n\
+socket, listen on it and accept connections. Once the connection is\n\
+established, the application would construct a SocketSessionReceiver\n\
+object with the socket for the newly established connection. This\n\
+behavior is based on the design decision that the application should\n\
+decide when it performs (possibly) blocking operations (see\n\
+socketsession module for more details).\n\
+\n\
+See the description of socketsession module for other details of how\n\
+the session forwarding works.\n\
+\n\
+SocketSessionReceiver(socket)\n\
+\n\
+ The constructor.\n\
+\n\
+ Exceptions:\n\
+ TypeError The given parameter is not a valid socket object\n\
+ SocketSessionError Any error on an operation that is performed\n\
+ on the given socket as part of initialization.\n\
+ SystemError Unexpected errors such as resource allocation failure\n\
+\n\
+ Parameters:\n\
+ socket A python socket object of a UNIX domain family for an\n\
+ established connection with a forwarder.\n\
+\n\
+";
+
+// Modifications
+// - socket session utility -> module
+// - return value (not a SocketSession object, but a Python tuple)
+// - remove the validity note (we copy it here, so there's no such
+// restriction)
+// - caller's responsibility: only responsible for closing the socket.
+// - text around the bullets
+// - exception
+const char* const SocketSessionReceiver_pop_doc = "\
+pop() -> (socket, socket address, socket address, byte)\n\
+\n\
+Receive a socket session from the forwarder.\n\
+\n\
+This method receives wire-format data (see socketsession module) for\n\
+a socket session on the UNIX domain socket, performs some validation\n\
+on the data, and returns the session information as a tuple.\n\
+\n\
+The caller is responsible for closing the received socket.\n\
+\n\
+It ensures the following:\n\
+\n\
+- The socket's address family is either AF_INET or AF_INET6\n\
+- The family element of the socket addresses for the local and remote\n\
+ end points must be equal to the socket's address family\n\
+- The socket session data is not empty and does not exceed 65535\n\
+ bytes.\n\
+\n\
+If the validation fails or an unexpected system error happens\n\
+(including a connection close in the meddle of reception), it throws\n\
+an SocketSessionError exception. When this happens, it's very\n\
+unlikely that a subsequent call to this method succeeds, so in\n\
+reality the application is expected to destruct it and close the\n\
+socket in such a case.\n\
+\n\
+Exceptions:\n\
+ SocketSessionError Invalid data is received or a system error on\n\
+ socket operation happens.\n\
+ SystemError Unexpected errors such as resource allocation failure\n\
+\n\
+Return Value(s): A tuple corresponding to the extracted socket session:\n\
+ socket A Python socket object corresponding to the socket passed\n\
+ by the forwarder\n\
+ socket address A Python socket address (which is a tuple) for the local\n\
+ end point\n\
+ socket address A Python socket address for the remote endpoint\n\
+ data A Python byte object that stores the session data\n\
+";
+} // unnamed namespace
diff --git a/src/lib/python/isc/util/cio/socketsessionreceiver_python.cc b/src/lib/python/isc/util/cio/socketsessionreceiver_python.cc
new file mode 100644
index 0000000..c79f6e0
--- /dev/null
+++ b/src/lib/python/isc/util/cio/socketsessionreceiver_python.cc
@@ -0,0 +1,327 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// Enable this if you use s# variants with PyArg_ParseTuple(), see
+// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
+//#define PY_SSIZE_T_CLEAN
+
+// Python.h needs to be placed at the head of the program file, see:
+// http://docs.python.org/py3k/extending/extending.html#a-simple-example
+#include <Python.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <string>
+#include <stdexcept>
+
+#include <boost/lexical_cast.hpp>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <util/io/socketsession.h>
+
+#include "socketsession_python.h"
+#include "socketsessionreceiver_python.h"
+
+using namespace std;
+using namespace isc::util::python;
+using namespace isc::util::io;
+using namespace isc::util::io::python;
+using boost::lexical_cast;
+
+// Trivial constructor.
+s_SocketSessionReceiver::s_SocketSessionReceiver() : cppobj(NULL) {
+}
+
+// Import pydoc text
+#include "socketsessionreceiver_inc.cc"
+
+namespace {
+// This C structure corresponds to a Python callable object for
+// socket.fromfd().
+// See json_dumps_obj in dns_requestloader_python.cc for background rationale
+// of this trick.
+PyObject* socket_fromfd_obj = NULL;
+
+int
+SocketSessionReceiver_init(PyObject* po_self, PyObject* args, PyObject*) {
+ s_SocketSessionReceiver* self =
+ static_cast<s_SocketSessionReceiver*>(po_self);
+ try {
+ // The constructor expects a Python socket object. We'll extract
+ // the underlying file descriptor using the fileno method (in the
+ // duck typing manner) and pass it to the C++ constructor.
+ // PyObject_CallMethod() could return NULL (especially if the given
+ // object is of the wrong type and doesn't have the "fileno" method),
+ // in which case PyObjectContainer will detect it and throw
+ // PyCPPWrapperException, which will be converted to the Python
+ // TypeError below.
+ PyObject* po_sock;
+ if (PyArg_ParseTuple(args, "O", &po_sock)) {
+ PyObjectContainer fd_container(PyObject_CallMethod(
+ po_sock,
+ const_cast<char*>("fileno"),
+ NULL));
+ PyObjectContainer fdarg_container(
+ Py_BuildValue("(O)", fd_container.get()));
+ int fd;
+ if (PyArg_ParseTuple(fdarg_container.get(), "i", &fd)) {
+ self->cppobj = new SocketSessionReceiver(fd);
+ return (0);
+ }
+ PyErr_SetString(PyExc_TypeError, "Given object's fileno() doesn't "
+ "return an integer, probably not a valid socket "
+ "object");
+ }
+ } catch (const PyCPPWrapperException& ex) {
+ // This could happen due to memory allocation failure, but it's more
+ // likely that the object doesn't have the "fileno()" method or it
+ // returns an unexpected type of value. So we adjust the error
+ // message accordingly.
+ PyErr_SetString(PyExc_TypeError, "Failed to parse parameter, "
+ "probably not a valid socket object");
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Failed to construct SocketSessionReceiver object: " +
+ string(ex.what());
+ PyErr_SetString(po_SocketSessionError, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
+ }
+
+ return (-1);
+}
+
+PyObject*
+createPySocketAddress(const struct sockaddr& sa) {
+ socklen_t salen;
+ if (sa.sa_family == AF_INET) {
+ salen = sizeof(struct sockaddr_in);
+ } else if (sa.sa_family == AF_INET6) {
+ salen = sizeof(struct sockaddr_in6);
+ } else {
+ isc_throw(SocketSessionError, "Unsupported socket address family: "
+ << static_cast<int>(sa.sa_family));
+ }
+
+ char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
+ const int error = getnameinfo(&sa, salen, hbuf, sizeof(hbuf), sbuf,
+ sizeof(sbuf),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (error != 0) {
+ isc_throw(SocketSessionError, "Unrecognized socket address format: "
+ << gai_strerror(error));
+ }
+ if (sa.sa_family == AF_INET) {
+ return (Py_BuildValue("(si)", hbuf, lexical_cast<int>(sbuf)));
+ }
+ // We know it's AF_INET6 at this point. We need some special trick for
+ // non-0 scope (zone) ID: getnameinfo() may convert the address to a
+ // textual representation using the extension described in RFC 4007,
+ // in which case it contains a delimiter character '%'. We need to remove
+ // it before constructing the tuple. The scope (zone) ID is preserved
+ // in the corresponding field of the tuple.
+ const void* p = &sa;
+ const struct sockaddr_in6* sin6 =
+ static_cast<const struct sockaddr_in6*>(p);
+ char* cp = strchr(hbuf, '%');
+ if (cp != NULL) {
+ *cp = '\0';
+ }
+ return (Py_BuildValue("(siii)", hbuf, lexical_cast<int>(sbuf), 0,
+ sin6->sin6_scope_id));
+}
+
+void
+SocketSessionReceiver_destroy(PyObject* po_self) {
+ s_SocketSessionReceiver* self =
+ static_cast<s_SocketSessionReceiver*>(po_self);
+ delete self->cppobj;
+ self->cppobj = NULL;
+ Py_TYPE(self)->tp_free(self);
+}
+
+// A helper struct to automatically close a socket in an RAII manner.
+struct ScopedSocket : boost::noncopyable {
+ ScopedSocket(int fd) : fd_(fd) {}
+ ~ScopedSocket() {
+ close(fd_);
+ }
+ const int fd_;
+};
+
+PyObject*
+SocketSessionReceiver_pop(PyObject* po_self, PyObject*) {
+ s_SocketSessionReceiver* const self =
+ static_cast<s_SocketSessionReceiver*>(po_self);
+
+ try {
+ // retrieve the session, and the convert it to a corresponding
+ // Python tuple.
+ const SocketSession session = self->cppobj->pop();
+
+ // We need to immediately store the socket file descriptor in a
+ // ScopedSocket object. socket.fromfd() will dup() the FD, so we need
+ // to close our copy even if an exception is thrown.
+ ScopedSocket sock(session.getSocket());
+
+ // Build Python socket object
+ PyObjectContainer c_args(Py_BuildValue("(iiii)", sock.fd_,
+ session.getFamily(),
+ session.getType(),
+ session.getProtocol()));
+ PyObjectContainer c_sock(PyObject_CallObject(socket_fromfd_obj,
+ c_args.get()));
+ // Convert the local and remote sockaddr to Python socket address objs
+ PyObjectContainer c_local(createPySocketAddress(
+ session.getLocalEndpoint()));
+ PyObjectContainer c_remote(createPySocketAddress(
+ session.getRemoteEndpoint()));
+ // Convert the session data to Python byte object.
+ PyObjectContainer c_data(Py_BuildValue("y#", session.getData(),
+ session.getDataLength()));
+
+ // Build a tuple from them and return it.
+ return (Py_BuildValue("(OOOO)", c_sock.get(), c_local.get(),
+ c_remote.get(), c_data.get()));
+ } catch (const SocketSessionError& ex) {
+ PyErr_SetString(po_SocketSessionError, ex.what());
+ return (NULL);
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Unexpected failure in receiving a socket session: " +
+ string(ex.what());
+ PyErr_SetString(PyExc_SystemError, ex_what.c_str());
+ return (NULL);
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
+ return (NULL);
+ }
+}
+
+// These are the functions we export
+
+// This list contains the actual set of functions we have in
+// python. Each entry has
+// 1. Python method name
+// 2. Our static function here
+// 3. Argument type
+// 4. Documentation
+PyMethodDef SocketSessionReceiver_methods[] = {
+ { "pop", SocketSessionReceiver_pop, METH_NOARGS,
+ SocketSessionReceiver_pop_doc },
+ { NULL, NULL, 0, NULL }
+};
+} // end of unnamed namespace
+
+namespace isc {
+namespace util {
+namespace io {
+namespace python {
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_SocketSessionReceiver
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject socketsessionreceiver_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "isc.util.cio.SocketSessionReceiver",
+ sizeof(s_SocketSessionReceiver), // tp_basicsize
+ 0, // tp_itemsize
+ SocketSessionReceiver_destroy, // tp_dealloc
+ NULL, // tp_print
+ NULL, // tp_getattr
+ NULL, // tp_setattr
+ NULL, // tp_reserved
+ NULL, // tp_repr
+ NULL, // tp_as_number
+ NULL, // tp_as_sequence
+ NULL, // tp_as_mapping
+ NULL, // tp_hash
+ NULL, // tp_call
+ NULL, // tp_str
+ NULL, // tp_getattro
+ NULL, // tp_setattro
+ NULL, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ SocketSessionReceiver_doc,
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ NULL, // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ SocketSessionReceiver_methods, // tp_methods
+ NULL, // tp_members
+ NULL, // tp_getset
+ NULL, // tp_base
+ NULL, // tp_dict
+ NULL, // tp_descr_get
+ NULL, // tp_descr_set
+ 0, // tp_dictoffset
+ SocketSessionReceiver_init, // tp_init
+ NULL, // tp_alloc
+ PyType_GenericNew, // tp_new
+ NULL, // tp_free
+ NULL, // tp_is_gc
+ NULL, // tp_bases
+ NULL, // tp_mro
+ NULL, // tp_cache
+ NULL, // tp_subclasses
+ NULL, // tp_weaklist
+ NULL, // tp_del
+ 0 // tp_version_tag
+};
+
+// Module Initialization, all statics are initialized here
+bool
+initModulePart_SocketSessionReceiver(PyObject* mod) {
+ // We initialize the static description object with PyType_Ready(),
+ // then add it to the module. This is not just a check! (leaving
+ // this out results in segmentation faults)
+ if (PyType_Ready(&socketsessionreceiver_type) < 0) {
+ return (false);
+ }
+ void* p = &socketsessionreceiver_type;
+ if (PyModule_AddObject(mod, "SocketSessionReceiver",
+ static_cast<PyObject*>(p)) < 0) {
+ return (false);
+ }
+
+ PyObject* socket_module = PyImport_AddModule("socket");
+ if (socket_module != NULL) {
+ PyObject* socket_dict = PyModule_GetDict(socket_module);
+ if (socket_dict != NULL) {
+ socket_fromfd_obj = PyDict_GetItemString(socket_dict, "fromfd");
+ }
+ }
+ if (socket_fromfd_obj != NULL) {
+ Py_INCREF(socket_fromfd_obj);
+ } else {
+ PyErr_SetString(PyExc_RuntimeError,
+ "isc.util.cio.SocketSessionReceiver needs "
+ "socket.fromfd(), but it's missing");
+ return (false);
+ }
+
+ Py_INCREF(&socketsessionreceiver_type);
+
+ return (true);
+}
+
+} // namespace python
+} // namespace io
+} // namespace util
+} // namespace isc
diff --git a/src/lib/python/isc/util/cio/socketsessionreceiver_python.h b/src/lib/python/isc/util/cio/socketsessionreceiver_python.h
new file mode 100644
index 0000000..14e8a1b
--- /dev/null
+++ b/src/lib/python/isc/util/cio/socketsessionreceiver_python.h
@@ -0,0 +1,46 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_SOCKETSESSIONRECEIVER_H
+#define __PYTHON_SOCKETSESSIONRECEIVER_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace util {
+namespace io {
+class SocketSessionReceiver;
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_SocketSessionReceiver : public PyObject {
+public:
+ s_SocketSessionReceiver();
+ SocketSessionReceiver* cppobj;
+};
+
+extern PyTypeObject socketsessionreceiver_type;
+
+bool initModulePart_SocketSessionReceiver(PyObject* mod);
+
+} // namespace io
+} // namespace python
+} // namespace util
+} // namespace isc
+#endif // __PYTHON_SOCKETSESSIONRECEIVER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/python/isc/util/cio/tests/Makefile.am b/src/lib/python/isc/util/cio/tests/Makefile.am
new file mode 100644
index 0000000..3429009
--- /dev/null
+++ b/src/lib/python/isc/util/cio/tests/Makefile.am
@@ -0,0 +1,36 @@
+PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
+PYTESTS = socketsession_test.py
+EXTRA_DIST = $(PYTESTS)
+
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
+# test using command-line arguments, so use check-local target instead of TESTS
+check-local:
+if ENABLE_PYTHON_COVERAGE
+ touch $(abs_top_srcdir)/.coverage
+ rm -f .coverage
+ ${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
+# Note: below we intentionally use a non absolute path for TESTDATAOBJDIR.
+# It will be used as part of the path for a UNIX domain socket. Due to the
+# relatively lower limit on the length it's better to keep it as short as
+# possible.
+ for pytest in $(PYTESTS) ; do \
+ echo Running test: $$pytest ; \
+ PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/isc/python/util/io/.libs \
+ $(LIBRARY_PATH_PLACEHOLDER) \
+ TESTDATAOBJDIR=$(builddir) \
+ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
+ done
+
+CLEANFILES = $(builddir)/ssessiontest.unix
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/util/cio/tests/socketsession_test.py b/src/lib/python/isc/util/cio/tests/socketsession_test.py
new file mode 100644
index 0000000..66b43d5
--- /dev/null
+++ b/src/lib/python/isc/util/cio/tests/socketsession_test.py
@@ -0,0 +1,253 @@
+# Copyright (C) 2011 Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import os, signal, socket, unittest
+from socket import AF_INET, AF_INET6, SOCK_STREAM, SOCK_DGRAM, IPPROTO_UDP, \
+ IPPROTO_TCP
+from isc.util.cio.socketsession import *
+
+TESTDATA_OBJDIR = os.getenv("TESTDATAOBJDIR")
+TEST_UNIX_FILE = TESTDATA_OBJDIR + '/ssessiontest.unix'
+TEST_DATA = b'BIND10 test'
+TEST_PORT = 53535
+
+class TestForwarder(unittest.TestCase):
+ '''In general, this is a straightforward port of the C++ counterpart.
+
+ In some cases test cases are simplified or have Python specific cases.
+
+ '''
+
+ def setUp(self):
+ self.forwarder = SocketSessionForwarder(TEST_UNIX_FILE)
+ if os.path.exists(TEST_UNIX_FILE):
+ os.unlink(TEST_UNIX_FILE)
+ self.large_text = b'a' * 65535
+
+ def tearDown(self):
+ if os.path.exists(TEST_UNIX_FILE):
+ os.unlink(TEST_UNIX_FILE)
+
+ def start_listen(self):
+ self.listen_sock = socket.socket(socket.AF_UNIX, SOCK_STREAM, 0)
+ self.listen_sock.bind(TEST_UNIX_FILE)
+ self.listen_sock.listen(10)
+
+ def accept_forwarder(self):
+ self.listen_sock.setblocking(False)
+ s, _ = self.listen_sock.accept()
+ s.setblocking(True)
+ return s
+
+ def test_init(self):
+ # check bad arguments. valid cases will covered in other tests.
+ self.assertRaises(TypeError, SocketSessionForwarder, 1)
+ self.assertRaises(TypeError, SocketSessionForwarder,
+ 'test.unix', 'test.unix')
+
+ def test_badpush(self):
+ # bad numbers of parameters
+ self.assertRaises(TypeError, self.forwarder.push, 1)
+ self.assertRaises(TypeError, self.forwarder.push, 0, AF_INET,
+ SOCK_DGRAM, IPPROTO_UDP, ('127.0.0.1', 53),
+ ('192.0.2.1', 5300), TEST_DATA, 0)
+ # contain a bad type of parameter
+ self.assertRaises(TypeError, self.forwarder.push, 0, 'AF_INET',
+ SOCK_DGRAM, IPPROTO_UDP, ('127.0.0.1', 53),
+ ('192.0.2.1', 5300), TEST_DATA)
+ # bad local address
+ self.assertRaises(TypeError, self.forwarder.push, 0, AF_INET,
+ SOCK_DGRAM, IPPROTO_UDP, ('127.0.0..1', 53),
+ ('192.0.2.1', 5300), TEST_DATA)
+ self.assertRaises(TypeError, self.forwarder.push, 0, AF_INET,
+ SOCK_DGRAM, IPPROTO_UDP, '127.0.0.1',
+ ('192.0.2.1', 5300), TEST_DATA)
+ # bad remote address
+ self.assertRaises(TypeError, self.forwarder.push, 0, AF_INET6,
+ SOCK_DGRAM, IPPROTO_UDP, ('2001:db8::1', 53),
+ ('2001:db8:::3', 5300), TEST_DATA)
+
+ # push before connect
+ self.assertRaises(TypeError, self.forwarder.push, 0, AF_INET,
+ SOCK_DGRAM, IPPROTO_UDP, ('192.0.2.1', 53),
+ ('192.0.2.2', 53), TEST_DATA)
+
+ # Now connect the forwarder for the rest of tests
+ self.start_listen()
+ self.forwarder.connect_to_receiver()
+
+ # Inconsistent address family
+ self.assertRaises(TypeError, self.forwarder.push, 1, AF_INET,
+ SOCK_DGRAM, IPPROTO_UDP, ('2001:db8::1', 53, 0, 1),
+ ('192.0.2.2', 53), TEST_DATA)
+ self.assertRaises(TypeError, self.forwarder.push, 1, AF_INET6,
+ SOCK_DGRAM, IPPROTO_UDP, ('2001:db8::1', 53, 0, 1),
+ ('192.0.2.2', 53), TEST_DATA)
+
+ # Empty data: we reject them at least for now
+ self.assertRaises(TypeError, self.forwarder.push, 1, AF_INET,
+ SOCK_DGRAM, IPPROTO_UDP, ('192.0.2.1', 53),
+ ('192.0.2.2', 53), b'')
+
+ # Too big data: we reject them at least for now
+ self.assertRaises(TypeError, self.forwarder.push, 1, AF_INET,
+ SOCK_DGRAM, IPPROTO_UDP, ('192.0.2.1', 53),
+ ('192.0.2.2', 53), b'd' * 65536)
+
+ # Close the receptor before push. It will result in SIGPIPE (should be
+ # ignored) and EPIPE, which will be converted to SocketSessionError.
+ self.listen_sock.close()
+ self.assertRaises(SocketSessionError, self.forwarder.push, 1, AF_INET,
+ SOCK_DGRAM, IPPROTO_UDP, ('192.0.2.1', 53),
+ ('192.0.2.2', 53), TEST_DATA)
+
+ def create_socket(self, family, type, protocol, addr, do_listen):
+ s = socket.socket(family, type, protocol)
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ s.bind(addr)
+ if do_listen and protocol == IPPROTO_TCP:
+ s.listen(1)
+ return s
+
+ def check_push_and_pop(self, family, type, protocol, local, remote,
+ data, new_connection):
+ sock = self.create_socket(family, type, protocol, local, True)
+ fwd_fd = sock.fileno()
+ if protocol == IPPROTO_TCP:
+ client_addr = ('::1', 0, 0, 0) if family == AF_INET6 \
+ else ('127.0.0.1', 0)
+ client_sock = self.create_socket(family, type, protocol,
+ client_addr, False)
+ client_sock.setblocking(False)
+ try:
+ client_sock.connect(local)
+ except socket.error:
+ pass
+ server_sock, _ = sock.accept()
+ fwd_fd = server_sock.fileno()
+
+ # If a new connection is required, start the "server", have the
+ # internal forwarder connect to it, and then internally accept it.
+ if new_connection:
+ self.start_listen()
+ self.forwarder.connect_to_receiver()
+ self.accept_sock = self.accept_forwarder()
+
+ # Then push one socket session via the forwarder.
+ self.forwarder.push(fwd_fd, family, type, protocol, local, remote,
+ data)
+
+ # Pop the socket session we just pushed from a local receiver, and
+ # check the content.
+ receiver = SocketSessionReceiver(self.accept_sock)
+ signal.alarm(1)
+ sock_session = receiver.pop()
+ signal.alarm(0)
+ passed_sock = sock_session[0]
+ self.assertNotEqual(fwd_fd, passed_sock.fileno())
+ self.assertEqual(family, passed_sock.family)
+ self.assertEqual(type, passed_sock.type)
+ self.assertEqual(protocol, passed_sock.proto)
+ self.assertEqual(local, sock_session[1])
+ self.assertEqual(remote, sock_session[2])
+ self.assertEqual(data, sock_session[3])
+
+ # Check if the passed FD is usable by sending some data from it.
+ passed_sock.setblocking(True)
+ if protocol == IPPROTO_UDP:
+ self.assertEqual(len(TEST_DATA), passed_sock.sendto(TEST_DATA,
+ local))
+ sock.settimeout(10)
+ self.assertEqual(TEST_DATA, sock.recvfrom(len(TEST_DATA))[0])
+ else:
+ server_sock.close()
+ self.assertEqual(len(TEST_DATA), passed_sock.send(TEST_DATA))
+ client_sock.setblocking(True)
+ client_sock.settimeout(10)
+ self.assertEqual(TEST_DATA, client_sock.recv(len(TEST_DATA)))
+
+ def test_push_and_pop(self):
+ # This is a straightforward port of C++ pushAndPop test.
+ local6 = ('::1', TEST_PORT, 0, 0)
+ remote6 = ('2001:db8::1', 5300, 0, 0)
+ self.check_push_and_pop(AF_INET6, SOCK_DGRAM, IPPROTO_UDP,
+ local6, remote6, TEST_DATA, True)
+ self.check_push_and_pop(AF_INET6, SOCK_STREAM, IPPROTO_TCP,
+ local6, remote6, TEST_DATA, False)
+
+ local4 = ('127.0.0.1', TEST_PORT)
+ remote4 = ('192.0.2.2', 5300)
+ self.check_push_and_pop(AF_INET, SOCK_DGRAM, IPPROTO_UDP,
+ local4, remote4, TEST_DATA, False)
+ self.check_push_and_pop(AF_INET, SOCK_STREAM, IPPROTO_TCP,
+ local4, remote4, TEST_DATA, False)
+
+ self.check_push_and_pop(AF_INET6, SOCK_DGRAM, IPPROTO_UDP,
+ local6, remote6, self.large_text, False)
+ self.check_push_and_pop(AF_INET6, SOCK_STREAM, IPPROTO_TCP,
+ local6, remote6, self.large_text, False)
+ self.check_push_and_pop(AF_INET, SOCK_DGRAM, IPPROTO_UDP,
+ local4, remote4, self.large_text, False)
+ self.check_push_and_pop(AF_INET, SOCK_STREAM, IPPROTO_TCP,
+ local4, remote4, self.large_text, False)
+
+ # Python specific: check for an IPv6 scoped address with non 0
+ # scope (zone) ID
+ scope6 = ('fe80::1', TEST_PORT, 0, 1)
+ self.check_push_and_pop(AF_INET6, SOCK_DGRAM, IPPROTO_UDP,
+ local6, scope6, TEST_DATA, False)
+
+ def test_push_too_fast(self):
+ # A straightforward port of C++ pushTooFast test.
+ def multi_push(forwarder, addr, data):
+ for i in range(0, 10):
+ forwarder.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP, addr,
+ addr, data)
+ self.start_listen()
+ self.forwarder.connect_to_receiver()
+ self.assertRaises(SocketSessionError, multi_push, self.forwarder,
+ ('192.0.2.1', 53), self.large_text)
+
+ def test_bad_pop(self):
+ # This is a subset of C++ badPop test. We only check pop() raises
+ # SocketSessionError when it internally fails to get the FD.
+ # Other cases would require passing a valid FD from the test,
+ # which would make the test too complicated. As a wrapper checking
+ # one common failure case should be reasonably sufficient.
+
+ self.start_listen()
+ s = socket.socket(socket.AF_UNIX, SOCK_STREAM, 0)
+ s.setblocking(False)
+ s.connect(TEST_UNIX_FILE)
+ accept_sock = self.accept_forwarder()
+ receiver = SocketSessionReceiver(accept_sock)
+ s.close()
+ self.assertRaises(SocketSessionError, receiver.pop)
+
+class TestReceiver(unittest.TestCase):
+ # We only check a couple of failure cases on construction. Valid cases
+ # are covered in TestForwarder.
+
+ def test_bad_init(self):
+ class FakeSocket:
+ # pretending to be th standard socket class, but its fileno() is
+ # bogus.
+ def fileno(self):
+ return None
+ self.assertRaises(TypeError, SocketSessionReceiver, 1)
+ self.assertRaises(TypeError, SocketSessionReceiver, FakeSocket())
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/lib/python/isc/util/io/Makefile.am b/src/lib/python/isc/util/io/Makefile.am
deleted file mode 100644
index 6e2b263..0000000
--- a/src/lib/python/isc/util/io/Makefile.am
+++ /dev/null
@@ -1,41 +0,0 @@
-SUBDIRS = . tests
-
-AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
-AM_CPPFLAGS += $(BOOST_INCLUDES)
-AM_CXXFLAGS = $(B10_CXXFLAGS)
-
-python_PYTHON = __init__.py
-pythondir = $(PYTHON_SITEPKG_DIR)/isc/util/io
-
-pyexec_LTLIBRARIES = socketsession.la
-pyexecdir = $(PYTHON_SITEPKG_DIR)/isc/util/io
-
-socketsession_la_SOURCES = socketsession_python.cc socketsession_python.h
-socketsession_la_SOURCES += socketsessionforwarder_python.cc
-socketsession_la_SOURCES += socketsessionforwarder_python.h
-socketsession_la_SOURCES += socketsessionreceiver_python.cc
-socketsession_la_SOURCES += socketsessionreceiver_python.h
-socketsession_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
-socketsession_la_LDFLAGS = $(PYTHON_LDFLAGS)
-# Note: PYTHON_CXXFLAGS may have some -Wno... workaround, which must be
-# placed after -Wextra defined in AM_CXXFLAGS
-socketsession_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
-
-# Python prefers .so, while some OSes (specifically MacOS) use a different
-# suffix for dynamic objects. -module is necessary to work this around.
-socketsession_la_LDFLAGS += -module
-socketsession_la_LIBADD = $(top_builddir)/src/lib/util/io/libutil_io.la
-socketsession_la_LIBADD += $(PYTHON_LIB)
-
-# This is not installed, it helps locate the module during tests
-EXTRA_DIST = __init__.py socketsession.py
-
-EXTRA_DIST += socketsession_inc.cc
-EXTRA_DIST += socketsessionforwarder_inc.cc socketsessionreceiver_inc.cc
-
-CLEANFILES = __init__.pyc socketsession.pyc
-
-CLEANDIRS = __pycache__
-
-clean-local:
- rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/util/io/__init__.py b/src/lib/python/isc/util/io/__init__.py
deleted file mode 100644
index 935160a..0000000
--- a/src/lib/python/isc/util/io/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-"""
-Here are function and classes for forwarding socket sessions between processes.
-"""
diff --git a/src/lib/python/isc/util/io/socketsession.py b/src/lib/python/isc/util/io/socketsession.py
deleted file mode 100644
index ccb16ec..0000000
--- a/src/lib/python/isc/util/io/socketsession.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright (C) 2011 Internet Systems Consortium.
-#
-# Permission to use, copy, modify, and distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
-# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
-# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
-# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
-# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
-# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-# This file is not installed. See python/isc/log/__init__.py for the trick.
-
-import os
-import sys
-
-for base in sys.path[:]:
- libdir = os.path.join(base, 'isc/util/io/.libs')
- if os.path.exists(libdir):
- sys.path.insert(0, libdir)
-
-from socketsession import *
diff --git a/src/lib/python/isc/util/io/socketsession_inc.cc b/src/lib/python/isc/util/io/socketsession_inc.cc
deleted file mode 100644
index e200063..0000000
--- a/src/lib/python/isc/util/io/socketsession_inc.cc
+++ /dev/null
@@ -1,122 +0,0 @@
-namespace {
-const char* const socketsession_doc = "\
-This module defines a set of classes that support forwarding a\n\
-\"socket session\" from one process to another. A socket session is a\n\
-conceptual tuple of the following elements:\n\
-\n\
-- A network socket\n\
-- The local and remote endpoints of a (IP) communication taking place\n\
- on the socket. In practice an endpoint is a pair of an IP address\n\
- and TCP or UDP port number.\n\
-- Some amount of data sent from the remote endpoint and received on\n\
- the socket. We call it (socket) session data in this documentation.\n\
-\n\
-Note that this is a conceptual definition. Depending on the underlying\n\
-implementation and/or the network protocol, some of the elements could\n\
-be part of others; for example, if it's an established TCP connection,\n\
-the local and remote endpoints would be able to be retrieved from the\n\
-socket using the standard getsockname() and getpeername() system\n\
-calls. But in this definition we separate these to be more generic.\n\
-Also, as a matter of fact our intended usage includes non-connected\n\
-UDP communications, in which case at least the remote endpoint should\n\
-be provided separately from the socket.\n\
-\n\
-In the actual implementation we represent a socket as a Python socket\n\
-object, which contains the information of the address family\n\
-(e.g. AF_INET6), socket type (e.g. SOCK_STREAM), and protocol\n\
-(e.g. IPPROTO_TCP).\n\
-\n\
-We use the Python socket address tuple to represent endpoints.\n\
-\n\
-Socket session data is an opaque blob in the form of a Python byte\n\
-object.\n\
-\n\
-To forward a socket session between processes, we use connected UNIX\n\
-domain sockets established between the processes. The file descriptor\n\
-will be forwarded through the sockets as an ancillary data item of\n\
-type SCM_RIGHTS. Other elements of the session will be transferred as\n\
-normal data over the connection.\n\
-\n\
-We provide two classes to help applications forward socket sessions:\n\
-SocketSessionForwarder is the sender of the UNIX domain connection,\n\
-while SocketSessionReceiver is the receiver (this interface assumes\n\
-one direction of forwarding).\n\
-\n\
-Note: this paragraph and following discussions on the internal\n\
-protocol are for reference purposes only; it's not necessary to\n\
-understand how to use the API.\n\
-SocketSessionForwarder and SocketSessionReceiver objects (internally)\n\
-use a straightforward protocol to pass elements of socket sessions.\n\
-Once the connection is established, the forwarder object first forwards\n\
-the file descriptor with 1-byte dummy data. It then forwards a\n\
-\"(socket) session header\", which contains all other elements of\n\
-the session except the file descriptor (already forwarded) and session\n\
-data. The wire format of the header is as follows:\n\
-\n\
-- The length of the header (16-bit unsigned integer)\n\
-- Address family\n\
-- Socket type\n\
-- Protocol\n\
-- Size of the local endpoint in bytes\n\
-- Local endpoint (a copy of the memory image of the corresponding\n\
- sockaddr)\n\
-- Size of the remote endpoint in bytes\n\
-- Remote endpoint (same as local endpoint)\n\
-- Size of session data in bytes\n\
-\n\
-The type of the fields is 32-bit unsigned integer unless explicitly\n\
-noted, and all fields are formatted in the network byte order.\n\
-\n\
-The socket session data immediately follows the session header.\n\
-\n\
-Note that the fields do not necessarily be in the network byte order\n\
-because they are expected to be exchanged on the same machine.\n\
-Likewise, integer elements such as address family do not necessarily\n\
-be represented as an fixed-size value (i.e., 32-bit). But fixed size\n\
-fields are used in order to ensure maximum portability in such a\n\
-(rare) case where the forwarder and the receiver are built with\n\
-different compilers that have different definitions of int. Also,\n\
-since sockaddr fields are generally formatted in the network byte\n\
-order, other fields are defined so to be consistent.\n\
-\n\
-One basic assumption in the API of this module is socket sessions\n\
-should be forwarded without blocking, thus eliminating the need for\n\
-incremental read/write or blocking other important services such as\n\
-responding to requests from the application's clients. This assumption\n\
-should be held as long as both the forwarder and receiver have\n\
-sufficient resources to handle the forwarding process since the\n\
-communication is local. But a forward attempt could still block if the\n\
-receiver is busy (or even hang up) and cannot keep up with the volume\n\
-of incoming sessions.\n\
-\n\
-So, in this implementation, the forwarder uses non blocking writes to\n\
-forward sessions. If a write attempt could block, it immediately gives\n\
-up the operation with an exception. The corresponding application is\n\
-expected to catch it, close the connection, and perform any necessary\n\
-recovery for that application (that would normally be re-establish the\n\
-connection with a new receiver, possibly after confirming the\n\
-receiving side is still alive). On the other hand, the receiver\n\
-implementation assumes it's possible that it only receive incomplete\n\
-elements of a session (such as in the case where the forwarder writes\n\
-part of the entire session and gives up the connection). The receiver\n\
-implementation throws an exception when it encounters an incomplete\n\
-session. Like the case of the forwarder application, the receiver\n\
-application is expected to catch it, close the connection, and perform\n\
-any necessary recovery steps.\n\
-\n\
-Note that the receiver implementation uses blocking read. So it's\n\
-application's responsibility to ensure that there's at least some data\n\
-in the connection when the receiver object is requested to receive a\n\
-session (unless this operation can be blocking, e.g., by the use of a\n\
-separate thread). Also, if the forwarder implementation or application\n\
-is malicious or extremely buggy and intentionally sends partial\n\
-session and keeps the connection, the receiver could block in\n\
-receiving a session. In general, we assume the forwarder doesn't do\n\
-intentional blocking as it's a local node and is generally a module of\n\
-the same (BIND 10) system. The minimum requirement for the forwarder\n\
-implementation (and application) is to make sure the connection is\n\
-closed once it detects an error on it. Even a naive implementation\n\
-that simply dies due to the exception will meet this requirement.\n\
-\n\
-";
-} // unnamed namespace
diff --git a/src/lib/python/isc/util/io/socketsession_python.cc b/src/lib/python/isc/util/io/socketsession_python.cc
deleted file mode 100644
index 7011b53..0000000
--- a/src/lib/python/isc/util/io/socketsession_python.cc
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <Python.h>
-
-#include <util/python/pycppwrapper_util.h>
-
-#include "socketsessionreceiver_python.h"
-#include "socketsessionforwarder_python.h"
-
-using namespace isc::util::io::python;
-using namespace isc::util::python;
-
-#include "socketsession_inc.cc"
-
-namespace isc {
-namespace util {
-namespace io {
-namespace python {
-PyObject* po_SocketSessionError;
-}
-}
-}
-}
-
-namespace {
-
-PyModuleDef socketsession = {
- { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
- "isc.util.io.socketsession",
- socketsession_doc,
- -1,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL
-};
-} // end of unnamed namespace
-
-PyMODINIT_FUNC
-PyInit_socketsession(void) {
- PyObject* mod = PyModule_Create(&socketsession);
- if (mod == NULL) {
- return (NULL);
- }
-
- try {
- po_SocketSessionError =
- PyErr_NewException("isc.util.io.SocketSessionError", NULL, NULL);
- PyObjectContainer(po_SocketSessionError).
- installToModule(mod, "SocketSessionError");
- } catch (...) {
- Py_DECREF(mod);
- return (NULL);
- }
-
- if (!initModulePart_SocketSessionForwarder(mod)) {
- Py_DECREF(mod);
- return (NULL);
- }
- if (!initModulePart_SocketSessionReceiver(mod)) {
- Py_DECREF(mod);
- return (NULL);
- }
-
- return (mod);
-}
diff --git a/src/lib/python/isc/util/io/socketsession_python.h b/src/lib/python/isc/util/io/socketsession_python.h
deleted file mode 100644
index b0703ac..0000000
--- a/src/lib/python/isc/util/io/socketsession_python.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __PYTHON_SOCKETSESSION_H
-#define __PYTHON_SOCKETSESSION_H 1
-
-#include <Python.h>
-
-namespace isc {
-namespace util {
-namespace io {
-namespace python {
-
-extern PyObject* po_SocketSessionError;
-
-} // namespace python
-} // namespace io
-} // namespace util
-} // namespace isc
-#endif // __PYTHON_SOCKETSESSION_H
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/python/isc/util/io/socketsessionforwarder_inc.cc b/src/lib/python/isc/util/io/socketsessionforwarder_inc.cc
deleted file mode 100644
index 6b9de01..0000000
--- a/src/lib/python/isc/util/io/socketsessionforwarder_inc.cc
+++ /dev/null
@@ -1,136 +0,0 @@
-namespace {
-// Modifications:
-// reference to the module description (instead of "utility")
-// exception description
-const char* const SocketSessionForwarder_doc = "\
-The forwarder of socket sessions.\n\
-\n\
-An object of this class maintains a UNIX domain socket (normally\n\
-expected to be connected to a SocketSessionReceiver object) and\n\
-forwards socket sessions to the receiver.\n\
-\n\
-See the description of socketsession module for other details of how\n\
-the session forwarding works.\n\
-\n\
-SocketSessionForwarder(unix_file)\n\
-\n\
- The constructor.\n\
-\n\
- It's constructed with path information of the intended receiver,\n\
- but does not immediately establish a connection to the receiver;\n\
- connect_to_receiver() must be called to establish it. These are\n\
- separated so that an object of class can be initialized (possibly\n\
- as an attribute of a higher level application class object)\n\
- without knowing the receiver is ready for accepting new\n\
- forwarders. The separate connect interface allows the object to be\n\
- reused when it detects connection failure and tries to re-\n\
- establish it after closing the failed one.\n\
-\n\
- On construction, it also installs a signal filter for SIGPIPE to\n\
- ignore it. Since this class uses a stream-type connected UNIX\n\
- domain socket, if the receiver (abruptly) closes the connection a\n\
- subsequent write operation on the socket would trigger a SIGPIPE\n\
- signal, which kills the caller process by default. This behavior\n\
- would be undesirable in many cases, so this implementation always\n\
- disables the signal.\n\
-\n\
- This approach has some drawbacks, however; first, since signal\n\
- handling is process (or thread) wide, ignoring it may not what the\n\
- application wants. On the other hand, if the application changes\n\
- how the signal is handled after instantiating this class, the new\n\
- behavior affects the class operation. Secondly, even if ignoring\n\
- the signal is the desired operation, it's a waste to set the\n\
- filter every time this class object is constructed. It's\n\
- sufficient to do it once. We still adopt this behavior based on\n\
- the observation that in most cases applications would like to\n\
- ignore SIGPIPE (or simply doesn't care about it) and that this\n\
- class is not instantiated so often (so the wasteful setting\n\
- overhead should be marginal). On the other hand, doing it every\n\
- time is beneficial if the application is threaded and different\n\
- threads create different forwarder objects (and if signals work\n\
- per thread).\n\
-\n\
- Exceptions:\n\
- SocketSessionError unix_file is invalid as a path name of a UNIX\n\
- domain socket or error happens in setting a filter for\n\
- SIGPIPE (see above)\n\
- SystemError Unexpected errors such as resource allocation failure\n\
-\n\
- Parameters:\n\
- unix_file Path name of the receiver.\n\
-\n\
-";
-
-// Modifications:
-// exception description
-const char* const SocketSessionForwarder_connectToReceiver_doc = "\
-connect_to_receiver()\n\
-\n\
-Establish a connection to the receiver.\n\
-\n\
-This method establishes a connection to the receiver at the path given\n\
-on construction. It makes the underlying UNIX domain socket non\n\
-blocking, so this method (or subsequent push() calls) does not block.\n\
-\n\
-Exceptions:\n\
- TypeError The method is called while an already established\n\
- connection is still active.\n\
- SocketSessionError A system error in socket operation.\n\
- SystemError Unexpected errors such as resource allocation failure\n\
-\n\
-";
-
-// Modifications:
-// bullet description
-// parameters
-// exception description
-const char* const SocketSessionForwarder_push_doc = "\
-push(sock, family, type, protocol, local_end, remote_end, data)\n\
-\n\
-Forward a socket session to the receiver.\n\
-\n\
-This method takes a set of parameters that represent a single socket\n\
-session, renders them in the \"wire\" format according to the internal\n\
-protocol (see socketsession module) and forwards them to the\n\
-receiver through the UNIX domain connection.\n\
-\n\
-The connection must have been established by connect_to_receiver().\n\
-\n\
-For simplicity and for the convenience of detecting application\n\
-errors, this method imposes some restrictions on the parameters:\n\
-\n\
-- Socket family must be either AF_INET or AF_INET6\n\
-- The address family (sa_family) member of the local and remote end\n\
- points must be equal to the family parameter\n\
-- Socket session data must not be empty\n\
-- Data length must not exceed 65535\n\
-\n\
-These are not architectural limitation, and might be loosened in future\n\
-versions as we see the need for flexibility.\n\
-\n\
-Since the underlying UNIX domain socket is non blocking (see the\n\
-description for the constructor), a call to this method should either\n\
-return immediately or result in exception (in case of \"would\n\
-block\").\n\
-\n\
-Exceptions:\n\
- TypeError The method is called before establishing a connection or\n\
- given parameters are invalid, or the given socket address\n\
- is valid.\n\
- SocketSessionError A system error in socket operation, including the\n\
- case where the write operation would block.\n\
-\n\
-Parameters:\n\
- sock (int) The socket file descriptor\n\
- family (int) The address family (such as socket.AF_INET6) of the\n\
- socket\n\
- type (int) The socket type (such as socket.SOCK_DGRAM) of the\n\
- socket\n\
- protocol (int) The transport protocol (such as socket.IPPROTO_UDP)\n\
- of the socket\n\
- local_end (socket address) The local end point of the session\n\
- remote_end (socket address) The remote end point of the session\n\
- data (byte) the session data\n\
-\n\
-";
-} // unnamed namespace
diff --git a/src/lib/python/isc/util/io/socketsessionforwarder_python.cc b/src/lib/python/isc/util/io/socketsessionforwarder_python.cc
deleted file mode 100644
index bdc2bf7..0000000
--- a/src/lib/python/isc/util/io/socketsessionforwarder_python.cc
+++ /dev/null
@@ -1,309 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-// Enable this if you use s# variants with PyArg_ParseTuple(), see
-// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
-//#define PY_SSIZE_T_CLEAN
-
-// Python.h needs to be placed at the head of the program file, see:
-// http://docs.python.org/py3k/extending/extending.html#a-simple-example
-#include <Python.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-
-#include <string>
-#include <stdexcept>
-
-#include <boost/lexical_cast.hpp>
-
-#include <exceptions/exceptions.h>
-
-#include <util/io/sockaddr_util.h>
-#include <util/io/socketsession.h>
-#include <util/python/pycppwrapper_util.h>
-
-#include "socketsession_python.h"
-#include "socketsessionforwarder_python.h"
-
-using namespace std;
-using namespace isc::util::python;
-using namespace isc::util::io;
-using namespace isc::util::io::internal;
-using namespace isc::util::io::python;
-using boost::lexical_cast;
-
-// Trivial constructor.
-s_SocketSessionForwarder::s_SocketSessionForwarder() : cppobj(NULL) {
-}
-
-// Import pydoc text
-#include "socketsessionforwarder_inc.cc"
-
-// See python/isc/log/log.cc for the use of namespace
-namespace clang_unnamed_namespace_workaround {
-// Internal exception class thrown when address parsing fails
-class AddressParseError: public isc::Exception {
-public:
- AddressParseError(const char *file, size_t line, const char *what):
- isc::Exception(file, line, what) {}
-};
-}
-using namespace clang_unnamed_namespace_workaround;
-
-namespace {
-
-int
-SocketSessionForwarder_init(PyObject* po_self, PyObject* args, PyObject*) {
- s_SocketSessionForwarder* self =
- static_cast<s_SocketSessionForwarder*>(po_self);
- try {
- const char* unix_file;
- if (PyArg_ParseTuple(args, "s", &unix_file)) {
- self->cppobj = new SocketSessionForwarder(unix_file);
- return (0);
- }
- } catch (const exception& ex) {
- const string ex_what =
- "Failed to construct SocketSessionForwarder object: " +
- string(ex.what());
- PyErr_SetString(po_SocketSessionError, ex_what.c_str());
- return (-1);
- } catch (...) {
- PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
- return (-1);
- }
-
- return (-1);
-}
-
-void
-SocketSessionForwarder_destroy(PyObject* po_self) {
- s_SocketSessionForwarder* self =
- static_cast<s_SocketSessionForwarder*>(po_self);
- delete self->cppobj;
- self->cppobj = NULL;
- Py_TYPE(self)->tp_free(self);
-}
-
-// Convert a Python socket address object to an addrinfo structure by
-// getaddrinfo.
-void
-parsePySocketAddress(PyObject* obj, int type, int protocol,
- struct sockaddr_storage* ss)
-{
- struct addrinfo hints;
- memset(&hints, 0, sizeof(hints));
- hints.ai_socktype = type;
- hints.ai_protocol = protocol;
- hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
-
- const char* addr;
- int port, flowinfo, scopeid;
- struct addrinfo *res;
- if (PyArg_ParseTuple(obj, "si", &addr, &port)) {
- // Possibly an IPv4 address.
- hints.ai_family = AF_INET;
- const int error = getaddrinfo(addr,
- lexical_cast<string>(port).c_str(),
- &hints, &res);
- if (error == 0) {
- assert(res->ai_addrlen <= sizeof(*ss));
- memcpy(ss, res->ai_addr, res->ai_addrlen);
- return;
- }
- isc_throw(AddressParseError, "Invalid or unsupported socket address: "
- << gai_strerror(error));
- }
- PyErr_Clear();
- if (PyArg_ParseTuple(obj, "siii", &addr, &port, &flowinfo, &scopeid)) {
- // Possibly an IPv6 address. We ignore flowinfo.
- hints.ai_family = AF_INET6;
- const int error = getaddrinfo(addr,
- lexical_cast<string>(port).c_str(),
- &hints, &res);
- if (error == 0) {
- assert(res->ai_addrlen <= sizeof(*ss));
- memcpy(ss, res->ai_addr, res->ai_addrlen);
- void* p = ss;
- static_cast<struct sockaddr_in6*>(p)->sin6_scope_id = scopeid;
- return;
- }
- isc_throw(AddressParseError, "Invalid or unsupported socket address: "
- << gai_strerror(error));
- }
- PyErr_Clear();
- isc_throw(AddressParseError, "Invalid or unsupported socket address, must "
- "be AF_INET or AF_INET6 socket address.");
-}
-
-PyObject*
-SocketSessionForwarder_connectToReceiver(PyObject* po_self, PyObject*) {
- s_SocketSessionForwarder* const self =
- static_cast<s_SocketSessionForwarder*>(po_self);
-
- try {
- self->cppobj->connectToReceiver();
- Py_RETURN_NONE;
- } catch (const isc::BadValue& ex) {
- PyErr_SetString(PyExc_TypeError, ex.what());
- return (NULL);
- } catch (const SocketSessionError& ex) {
- PyErr_SetString(po_SocketSessionError, ex.what());
- return (NULL);
- } catch (const exception& ex) {
- const string ex_what =
- "Unexpected failure in connecting to receiver: " +
- string(ex.what());
- PyErr_SetString(PyExc_SystemError, ex_what.c_str());
- return (NULL);
- } catch (...) {
- PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
- return (NULL);
- }
-}
-
-PyObject*
-SocketSessionForwarder_push(PyObject* po_self, PyObject* args) {
- s_SocketSessionForwarder* const self =
- static_cast<s_SocketSessionForwarder*>(po_self);
-
- try {
- int fd, family, type, protocol;
- PyObject* po_local_end;
- PyObject* po_remote_end;
- Py_buffer py_buf;
-
- if (!PyArg_ParseTuple(args, "iiiiOOy*", &fd, &family, &type, &protocol,
- &po_local_end, &po_remote_end, &py_buf)) {
- return (NULL);
- }
- struct sockaddr_storage ss_local, ss_remote;
- parsePySocketAddress(po_local_end, type, protocol, &ss_local);
- parsePySocketAddress(po_remote_end, type, protocol, &ss_remote);
- self->cppobj->push(fd, family, type, protocol,
- *convertSockAddr(&ss_local),
- *convertSockAddr(&ss_remote),
- py_buf.buf, py_buf.len);
- Py_RETURN_NONE;
- } catch (const AddressParseError& ex) {
- PyErr_SetString(PyExc_TypeError, ex.what());
- return (NULL);
- } catch (const isc::BadValue& ex) {
- PyErr_SetString(PyExc_TypeError, ex.what());
- return (NULL);
- } catch (const SocketSessionError& ex) {
- PyErr_SetString(po_SocketSessionError, ex.what());
- return (NULL);
- } catch (...) {
- PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
- return (NULL);
- }
-}
-
-// This list contains the actual set of functions we have in
-// python. Each entry has
-// 1. Python method name
-// 2. Our static function here
-// 3. Argument type
-// 4. Documentation
-PyMethodDef SocketSessionForwarder_methods[] = {
- { "push", SocketSessionForwarder_push, METH_VARARGS,
- SocketSessionForwarder_push_doc },
- { "connect_to_receiver", SocketSessionForwarder_connectToReceiver,
- METH_NOARGS, SocketSessionForwarder_connectToReceiver_doc },
- { NULL, NULL, 0, NULL }
-};
-} // end of unnamed namespace
-
-namespace isc {
-namespace util {
-namespace io {
-namespace python {
-// This defines the complete type for reflection in python and
-// parsing of PyObject* to s_SocketSessionForwarder
-// Most of the functions are not actually implemented and NULL here.
-PyTypeObject socketsessionforwarder_type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- "isc.util.io.SocketSessionForwarder",
- sizeof(s_SocketSessionForwarder), // tp_basicsize
- 0, // tp_itemsize
- SocketSessionForwarder_destroy, // tp_dealloc
- NULL, // tp_print
- NULL, // tp_getattr
- NULL, // tp_setattr
- NULL, // tp_reserved
- NULL, // tp_repr
- NULL, // tp_as_number
- NULL, // tp_as_sequence
- NULL, // tp_as_mapping
- NULL, // tp_hash
- NULL, // tp_call
- NULL, // tp_str
- NULL, // tp_getattro
- NULL, // tp_setattro
- NULL, // tp_as_buffer
- Py_TPFLAGS_DEFAULT, // tp_flags
- SocketSessionForwarder_doc,
- NULL, // tp_traverse
- NULL, // tp_clear
- NULL, // tp_richcompare
- 0, // tp_weaklistoffset
- NULL, // tp_iter
- NULL, // tp_iternext
- SocketSessionForwarder_methods, // tp_methods
- NULL, // tp_members
- NULL, // tp_getset
- NULL, // tp_base
- NULL, // tp_dict
- NULL, // tp_descr_get
- NULL, // tp_descr_set
- 0, // tp_dictoffset
- SocketSessionForwarder_init, // tp_init
- NULL, // tp_alloc
- PyType_GenericNew, // tp_new
- NULL, // tp_free
- NULL, // tp_is_gc
- NULL, // tp_bases
- NULL, // tp_mro
- NULL, // tp_cache
- NULL, // tp_subclasses
- NULL, // tp_weaklist
- NULL, // tp_del
- 0 // tp_version_tag
-};
-
-// Module Initialization, all statics are initialized here
-bool
-initModulePart_SocketSessionForwarder(PyObject* mod) {
- // We initialize the static description object with PyType_Ready(),
- // then add it to the module. This is not just a check! (leaving
- // this out results in segmentation faults)
- if (PyType_Ready(&socketsessionforwarder_type) < 0) {
- return (false);
- }
- void* p = &socketsessionforwarder_type;
- if (PyModule_AddObject(mod, "SocketSessionForwarder",
- static_cast<PyObject*>(p)) < 0) {
- return (false);
- }
- Py_INCREF(&socketsessionforwarder_type);
-
- return (true);
-}
-} // namespace python
-} // namespace io
-} // namespace util
-} // namespace isc
diff --git a/src/lib/python/isc/util/io/socketsessionforwarder_python.h b/src/lib/python/isc/util/io/socketsessionforwarder_python.h
deleted file mode 100644
index 2ce220a..0000000
--- a/src/lib/python/isc/util/io/socketsessionforwarder_python.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __PYTHON_SOCKETSESSIONFORWARDER_H
-#define __PYTHON_SOCKETSESSIONFORWARDER_H 1
-
-#include <Python.h>
-
-namespace isc {
-namespace util {
-namespace io {
-class SocketSessionForwarder;
-
-namespace python {
-
-// The s_* Class simply covers one instantiation of the object
-class s_SocketSessionForwarder : public PyObject {
-public:
- s_SocketSessionForwarder();
- SocketSessionForwarder* cppobj;
-};
-
-extern PyTypeObject socketsessionforwarder_type;
-
-bool initModulePart_SocketSessionForwarder(PyObject* mod);
-} // namespace python
-} // namespace io
-} // namespace util
-} // namespace isc
-#endif // __PYTHON_SOCKETSESSIONFORWARDER_H
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/python/isc/util/io/socketsessionreceiver_inc.cc b/src/lib/python/isc/util/io/socketsessionreceiver_inc.cc
deleted file mode 100644
index ed29d3e..0000000
--- a/src/lib/python/isc/util/io/socketsessionreceiver_inc.cc
+++ /dev/null
@@ -1,89 +0,0 @@
-namespace {
-// Modifications
-// - about return value
-// - socket session "utility" => module
-const char* const SocketSessionReceiver_doc = "\
-The receiver of socket sessions.\n\
-\n\
-An object of this class holds a UNIX domain socket for an established\n\
-connection, receives socket sessions from the remote forwarder, and\n\
-provides the session to the application as a tuple of corresponding\n\
-elements.\n\
-\n\
-Note that this class is instantiated with an already connected socket;\n\
-it's not a listening socket that is accepting connection requests from\n\
-forwarders. It's application's responsibility to create the listening\n\
-socket, listen on it and accept connections. Once the connection is\n\
-established, the application would construct a SocketSessionReceiver\n\
-object with the socket for the newly established connection. This\n\
-behavior is based on the design decision that the application should\n\
-decide when it performs (possibly) blocking operations (see\n\
-socketsession module for more details).\n\
-\n\
-See the description of socketsession module for other details of how\n\
-the session forwarding works.\n\
-\n\
-SocketSessionReceiver(socket)\n\
-\n\
- The constructor.\n\
-\n\
- Exceptions:\n\
- TypeError The given parameter is not a valid socket object\n\
- SocketSessionError Any error on an operation that is performed\n\
- on the given socket as part of initialization.\n\
- SystemError Unexpected errors such as resource allocation failure\n\
-\n\
- Parameters:\n\
- socket A python socket object of a UNIX domain family for an\n\
- established connection with a forwarder.\n\
-\n\
-";
-
-// Modifications
-// - socket session utility -> module
-// - return value (not a SocketSession object, but a Python tuple)
-// - remove the validity note (we copy it here, so there's no such
-// restriction)
-// - caller's responsibility: only responsible for closing the socket.
-// - text around the bullets
-// - exception
-const char* const SocketSessionReceiver_pop_doc = "\
-pop() -> (socket, socket address, socket address, byte)\n\
-\n\
-Receive a socket session from the forwarder.\n\
-\n\
-This method receives wire-format data (see socketsession module) for\n\
-a socket session on the UNIX domain socket, performs some validation\n\
-on the data, and returns the session information as a tuple.\n\
-\n\
-The caller is responsible for closing the received socket.\n\
-\n\
-It ensures the following:\n\
-\n\
-- The socket's address family is either AF_INET or AF_INET6\n\
-- The family element of the socket addresses for the local and remote\n\
- end points must be equal to the socket's address family\n\
-- The socket session data is not empty and does not exceed 65535\n\
- bytes.\n\
-\n\
-If the validation fails or an unexpected system error happens\n\
-(including a connection close in the meddle of reception), it throws\n\
-an SocketSessionError exception. When this happens, it's very\n\
-unlikely that a subsequent call to this method succeeds, so in\n\
-reality the application is expected to destruct it and close the\n\
-socket in such a case.\n\
-\n\
-Exceptions:\n\
- SocketSessionError Invalid data is received or a system error on\n\
- socket operation happens.\n\
- SystemError Unexpected errors such as resource allocation failure\n\
-\n\
-Return Value(s): A tuple corresponding to the extracted socket session:\n\
- socket A Python socket object corresponding to the socket passed\n\
- by the forwarder\n\
- socket address A Python socket address (which is a tuple) for the local\n\
- end point\n\
- socket address A Python socket address for the remote endpoint\n\
- data A Python byte object that stores the session data\n\
-";
-} // unnamed namespace
diff --git a/src/lib/python/isc/util/io/socketsessionreceiver_python.cc b/src/lib/python/isc/util/io/socketsessionreceiver_python.cc
deleted file mode 100644
index 24e3021..0000000
--- a/src/lib/python/isc/util/io/socketsessionreceiver_python.cc
+++ /dev/null
@@ -1,327 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-// Enable this if you use s# variants with PyArg_ParseTuple(), see
-// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
-//#define PY_SSIZE_T_CLEAN
-
-// Python.h needs to be placed at the head of the program file, see:
-// http://docs.python.org/py3k/extending/extending.html#a-simple-example
-#include <Python.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netdb.h>
-
-#include <string>
-#include <stdexcept>
-
-#include <boost/lexical_cast.hpp>
-
-#include <util/python/pycppwrapper_util.h>
-
-#include <util/io/socketsession.h>
-
-#include "socketsession_python.h"
-#include "socketsessionreceiver_python.h"
-
-using namespace std;
-using namespace isc::util::python;
-using namespace isc::util::io;
-using namespace isc::util::io::python;
-using boost::lexical_cast;
-
-// Trivial constructor.
-s_SocketSessionReceiver::s_SocketSessionReceiver() : cppobj(NULL) {
-}
-
-// Import pydoc text
-#include "socketsessionreceiver_inc.cc"
-
-namespace {
-// This C structure corresponds to a Python callable object for
-// socket.fromfd().
-// See json_dumps_obj in dns_requestloader_python.cc for background rationale
-// of this trick.
-PyObject* socket_fromfd_obj = NULL;
-
-int
-SocketSessionReceiver_init(PyObject* po_self, PyObject* args, PyObject*) {
- s_SocketSessionReceiver* self =
- static_cast<s_SocketSessionReceiver*>(po_self);
- try {
- // The constructor expects a Python socket object. We'll extract
- // the underlying file descriptor using the fileno method (in the
- // duck typing manner) and pass it to the C++ constructor.
- // PyObject_CallMethod() could return NULL (especially if the given
- // object is of the wrong type and doesn't have the "fileno" method),
- // in which case PyObjectContainer will detect it and throw
- // PyCPPWrapperException, which will be converted to the Python
- // TypeError below.
- PyObject* po_sock;
- if (PyArg_ParseTuple(args, "O", &po_sock)) {
- PyObjectContainer fd_container(PyObject_CallMethod(
- po_sock,
- const_cast<char*>("fileno"),
- NULL));
- PyObjectContainer fdarg_container(
- Py_BuildValue("(O)", fd_container.get()));
- int fd;
- if (PyArg_ParseTuple(fdarg_container.get(), "i", &fd)) {
- self->cppobj = new SocketSessionReceiver(fd);
- return (0);
- }
- PyErr_SetString(PyExc_TypeError, "Given object's fileno() doesn't "
- "return an integer, probably not a valid socket "
- "object");
- }
- } catch (const PyCPPWrapperException& ex) {
- // This could happen due to memory allocation failure, but it's more
- // likely that the object doesn't have the "fileno()" method or it
- // returns an unexpected type of value. So we adjust the error
- // message accordingly.
- PyErr_SetString(PyExc_TypeError, "Failed to parse parameter, "
- "probably not a valid socket object");
- } catch (const exception& ex) {
- const string ex_what =
- "Failed to construct SocketSessionReceiver object: " +
- string(ex.what());
- PyErr_SetString(po_SocketSessionError, ex_what.c_str());
- } catch (...) {
- PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
- }
-
- return (-1);
-}
-
-PyObject*
-createPySocketAddress(const struct sockaddr& sa) {
- socklen_t salen;
- if (sa.sa_family == AF_INET) {
- salen = sizeof(struct sockaddr_in);
- } else if (sa.sa_family == AF_INET6) {
- salen = sizeof(struct sockaddr_in6);
- } else {
- isc_throw(SocketSessionError, "Unsupported socket address family: "
- << static_cast<int>(sa.sa_family));
- }
-
- char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
- const int error = getnameinfo(&sa, salen, hbuf, sizeof(hbuf), sbuf,
- sizeof(sbuf),
- NI_NUMERICHOST | NI_NUMERICSERV);
- if (error != 0) {
- isc_throw(SocketSessionError, "Unrecognized socket address format: "
- << gai_strerror(error));
- }
- if (sa.sa_family == AF_INET) {
- return (Py_BuildValue("(si)", hbuf, lexical_cast<int>(sbuf)));
- }
- // We know it's AF_INET6 at this point. We need some special trick for
- // non-0 scope (zone) ID: getnameinfo() may convert the address to a
- // textual representation using the extension described in RFC 4007,
- // in which case it contains a delimiter character '%'. We need to remove
- // it before constructing the tuple. The scope (zone) ID is preserved
- // in the corresponding field of the tuple.
- const void* p = &sa;
- const struct sockaddr_in6* sin6 =
- static_cast<const struct sockaddr_in6*>(p);
- char* cp = strchr(hbuf, '%');
- if (cp != NULL) {
- *cp = '\0';
- }
- return (Py_BuildValue("(siii)", hbuf, lexical_cast<int>(sbuf), 0,
- sin6->sin6_scope_id));
-}
-
-void
-SocketSessionReceiver_destroy(PyObject* po_self) {
- s_SocketSessionReceiver* self =
- static_cast<s_SocketSessionReceiver*>(po_self);
- delete self->cppobj;
- self->cppobj = NULL;
- Py_TYPE(self)->tp_free(self);
-}
-
-// A helper struct to automatically close a socket in an RAII manner.
-struct ScopedSocket : boost::noncopyable {
- ScopedSocket(int fd) : fd_(fd) {}
- ~ScopedSocket() {
- close(fd_);
- }
- const int fd_;
-};
-
-PyObject*
-SocketSessionReceiver_pop(PyObject* po_self, PyObject*) {
- s_SocketSessionReceiver* const self =
- static_cast<s_SocketSessionReceiver*>(po_self);
-
- try {
- // retrieve the session, and the convert it to a corresponding
- // Python tuple.
- const SocketSession session = self->cppobj->pop();
-
- // We need to immediately store the socket file descriptor in a
- // ScopedSocket object. socket.fromfd() will dup() the FD, so we need
- // to close our copy even if an exception is thrown.
- ScopedSocket sock(session.getSocket());
-
- // Build Python socket object
- PyObjectContainer c_args(Py_BuildValue("(iiii)", sock.fd_,
- session.getFamily(),
- session.getType(),
- session.getProtocol()));
- PyObjectContainer c_sock(PyObject_CallObject(socket_fromfd_obj,
- c_args.get()));
- // Convert the local and remote sockaddr to Python socket address objs
- PyObjectContainer c_local(createPySocketAddress(
- session.getLocalEndpoint()));
- PyObjectContainer c_remote(createPySocketAddress(
- session.getRemoteEndpoint()));
- // Convert the session data to Python byte object.
- PyObjectContainer c_data(Py_BuildValue("y#", session.getData(),
- session.getDataLength()));
-
- // Build a tuple from them and return it.
- return (Py_BuildValue("(OOOO)", c_sock.get(), c_local.get(),
- c_remote.get(), c_data.get()));
- } catch (const SocketSessionError& ex) {
- PyErr_SetString(po_SocketSessionError, ex.what());
- return (NULL);
- } catch (const exception& ex) {
- const string ex_what =
- "Unexpected failure in receiving a socket session: " +
- string(ex.what());
- PyErr_SetString(PyExc_SystemError, ex_what.c_str());
- return (NULL);
- } catch (...) {
- PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
- return (NULL);
- }
-}
-
-// These are the functions we export
-
-// This list contains the actual set of functions we have in
-// python. Each entry has
-// 1. Python method name
-// 2. Our static function here
-// 3. Argument type
-// 4. Documentation
-PyMethodDef SocketSessionReceiver_methods[] = {
- { "pop", SocketSessionReceiver_pop, METH_NOARGS,
- SocketSessionReceiver_pop_doc },
- { NULL, NULL, 0, NULL }
-};
-} // end of unnamed namespace
-
-namespace isc {
-namespace util {
-namespace io {
-namespace python {
-// This defines the complete type for reflection in python and
-// parsing of PyObject* to s_SocketSessionReceiver
-// Most of the functions are not actually implemented and NULL here.
-PyTypeObject socketsessionreceiver_type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- "isc.util.io.SocketSessionReceiver",
- sizeof(s_SocketSessionReceiver), // tp_basicsize
- 0, // tp_itemsize
- SocketSessionReceiver_destroy, // tp_dealloc
- NULL, // tp_print
- NULL, // tp_getattr
- NULL, // tp_setattr
- NULL, // tp_reserved
- NULL, // tp_repr
- NULL, // tp_as_number
- NULL, // tp_as_sequence
- NULL, // tp_as_mapping
- NULL, // tp_hash
- NULL, // tp_call
- NULL, // tp_str
- NULL, // tp_getattro
- NULL, // tp_setattro
- NULL, // tp_as_buffer
- Py_TPFLAGS_DEFAULT, // tp_flags
- SocketSessionReceiver_doc,
- NULL, // tp_traverse
- NULL, // tp_clear
- NULL, // tp_richcompare
- 0, // tp_weaklistoffset
- NULL, // tp_iter
- NULL, // tp_iternext
- SocketSessionReceiver_methods, // tp_methods
- NULL, // tp_members
- NULL, // tp_getset
- NULL, // tp_base
- NULL, // tp_dict
- NULL, // tp_descr_get
- NULL, // tp_descr_set
- 0, // tp_dictoffset
- SocketSessionReceiver_init, // tp_init
- NULL, // tp_alloc
- PyType_GenericNew, // tp_new
- NULL, // tp_free
- NULL, // tp_is_gc
- NULL, // tp_bases
- NULL, // tp_mro
- NULL, // tp_cache
- NULL, // tp_subclasses
- NULL, // tp_weaklist
- NULL, // tp_del
- 0 // tp_version_tag
-};
-
-// Module Initialization, all statics are initialized here
-bool
-initModulePart_SocketSessionReceiver(PyObject* mod) {
- // We initialize the static description object with PyType_Ready(),
- // then add it to the module. This is not just a check! (leaving
- // this out results in segmentation faults)
- if (PyType_Ready(&socketsessionreceiver_type) < 0) {
- return (false);
- }
- void* p = &socketsessionreceiver_type;
- if (PyModule_AddObject(mod, "SocketSessionReceiver",
- static_cast<PyObject*>(p)) < 0) {
- return (false);
- }
-
- PyObject* socket_module = PyImport_AddModule("socket");
- if (socket_module != NULL) {
- PyObject* socket_dict = PyModule_GetDict(socket_module);
- if (socket_dict != NULL) {
- socket_fromfd_obj = PyDict_GetItemString(socket_dict, "fromfd");
- }
- }
- if (socket_fromfd_obj != NULL) {
- Py_INCREF(socket_fromfd_obj);
- } else {
- PyErr_SetString(PyExc_RuntimeError,
- "isc.util.io.SocketSessionReceiver needs "
- "socket.fromfd(), but it's missing");
- return (false);
- }
-
- Py_INCREF(&socketsessionreceiver_type);
-
- return (true);
-}
-
-} // namespace python
-} // namespace io
-} // namespace util
-} // namespace isc
diff --git a/src/lib/python/isc/util/io/socketsessionreceiver_python.h b/src/lib/python/isc/util/io/socketsessionreceiver_python.h
deleted file mode 100644
index 14e8a1b..0000000
--- a/src/lib/python/isc/util/io/socketsessionreceiver_python.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __PYTHON_SOCKETSESSIONRECEIVER_H
-#define __PYTHON_SOCKETSESSIONRECEIVER_H 1
-
-#include <Python.h>
-
-namespace isc {
-namespace util {
-namespace io {
-class SocketSessionReceiver;
-
-namespace python {
-
-// The s_* Class simply covers one instantiation of the object
-class s_SocketSessionReceiver : public PyObject {
-public:
- s_SocketSessionReceiver();
- SocketSessionReceiver* cppobj;
-};
-
-extern PyTypeObject socketsessionreceiver_type;
-
-bool initModulePart_SocketSessionReceiver(PyObject* mod);
-
-} // namespace io
-} // namespace python
-} // namespace util
-} // namespace isc
-#endif // __PYTHON_SOCKETSESSIONRECEIVER_H
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/python/isc/util/io/tests/Makefile.am b/src/lib/python/isc/util/io/tests/Makefile.am
deleted file mode 100644
index 3429009..0000000
--- a/src/lib/python/isc/util/io/tests/Makefile.am
+++ /dev/null
@@ -1,36 +0,0 @@
-PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
-PYTESTS = socketsession_test.py
-EXTRA_DIST = $(PYTESTS)
-
-# If necessary (rare cases), explicitly specify paths to dynamic libraries
-# required by loadable python modules.
-LIBRARY_PATH_PLACEHOLDER =
-if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
-endif
-
-# test using command-line arguments, so use check-local target instead of TESTS
-check-local:
-if ENABLE_PYTHON_COVERAGE
- touch $(abs_top_srcdir)/.coverage
- rm -f .coverage
- ${LN_S} $(abs_top_srcdir)/.coverage .coverage
-endif
-# Note: below we intentionally use a non absolute path for TESTDATAOBJDIR.
-# It will be used as part of the path for a UNIX domain socket. Due to the
-# relatively lower limit on the length it's better to keep it as short as
-# possible.
- for pytest in $(PYTESTS) ; do \
- echo Running test: $$pytest ; \
- PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/isc/python/util/io/.libs \
- $(LIBRARY_PATH_PLACEHOLDER) \
- TESTDATAOBJDIR=$(builddir) \
- $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
- done
-
-CLEANFILES = $(builddir)/ssessiontest.unix
-
-CLEANDIRS = __pycache__
-
-clean-local:
- rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/util/io/tests/socketsession_test.py b/src/lib/python/isc/util/io/tests/socketsession_test.py
deleted file mode 100644
index 9b5f12a..0000000
--- a/src/lib/python/isc/util/io/tests/socketsession_test.py
+++ /dev/null
@@ -1,253 +0,0 @@
-# Copyright (C) 2011 Internet Systems Consortium.
-#
-# Permission to use, copy, modify, and distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
-# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
-# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
-# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
-# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
-# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-import os, signal, socket, unittest
-from socket import AF_INET, AF_INET6, SOCK_STREAM, SOCK_DGRAM, IPPROTO_UDP, \
- IPPROTO_TCP
-from isc.util.io.socketsession import *
-
-TESTDATA_OBJDIR = os.getenv("TESTDATAOBJDIR")
-TEST_UNIX_FILE = TESTDATA_OBJDIR + '/ssessiontest.unix'
-TEST_DATA = b'BIND10 test'
-TEST_PORT = 53535
-
-class TestForwarder(unittest.TestCase):
- '''In general, this is a straightforward port of the C++ counterpart.
-
- In some cases test cases are simplified or have Python specific cases.
-
- '''
-
- def setUp(self):
- self.forwarder = SocketSessionForwarder(TEST_UNIX_FILE)
- if os.path.exists(TEST_UNIX_FILE):
- os.unlink(TEST_UNIX_FILE)
- self.large_text = b'a' * 65535
-
- def tearDown(self):
- if os.path.exists(TEST_UNIX_FILE):
- os.unlink(TEST_UNIX_FILE)
-
- def start_listen(self):
- self.listen_sock = socket.socket(socket.AF_UNIX, SOCK_STREAM, 0)
- self.listen_sock.bind(TEST_UNIX_FILE)
- self.listen_sock.listen(10)
-
- def accept_forwarder(self):
- self.listen_sock.setblocking(False)
- s, _ = self.listen_sock.accept()
- s.setblocking(True)
- return s
-
- def test_init(self):
- # check bad arguments. valid cases will covered in other tests.
- self.assertRaises(TypeError, SocketSessionForwarder, 1)
- self.assertRaises(TypeError, SocketSessionForwarder,
- 'test.unix', 'test.unix')
-
- def test_badpush(self):
- # bad numbers of parameters
- self.assertRaises(TypeError, self.forwarder.push, 1)
- self.assertRaises(TypeError, self.forwarder.push, 0, AF_INET,
- SOCK_DGRAM, IPPROTO_UDP, ('127.0.0.1', 53),
- ('192.0.2.1', 5300), TEST_DATA, 0)
- # contain a bad type of parameter
- self.assertRaises(TypeError, self.forwarder.push, 0, 'AF_INET',
- SOCK_DGRAM, IPPROTO_UDP, ('127.0.0.1', 53),
- ('192.0.2.1', 5300), TEST_DATA)
- # bad local address
- self.assertRaises(TypeError, self.forwarder.push, 0, AF_INET,
- SOCK_DGRAM, IPPROTO_UDP, ('127.0.0..1', 53),
- ('192.0.2.1', 5300), TEST_DATA)
- self.assertRaises(TypeError, self.forwarder.push, 0, AF_INET,
- SOCK_DGRAM, IPPROTO_UDP, '127.0.0.1',
- ('192.0.2.1', 5300), TEST_DATA)
- # bad remote address
- self.assertRaises(TypeError, self.forwarder.push, 0, AF_INET6,
- SOCK_DGRAM, IPPROTO_UDP, ('2001:db8::1', 53),
- ('2001:db8:::3', 5300), TEST_DATA)
-
- # push before connect
- self.assertRaises(TypeError, self.forwarder.push, 0, AF_INET,
- SOCK_DGRAM, IPPROTO_UDP, ('192.0.2.1', 53),
- ('192.0.2.2', 53), TEST_DATA)
-
- # Now connect the forwarder for the rest of tests
- self.start_listen()
- self.forwarder.connect_to_receiver()
-
- # Inconsistent address family
- self.assertRaises(TypeError, self.forwarder.push, 1, AF_INET,
- SOCK_DGRAM, IPPROTO_UDP, ('2001:db8::1', 53, 0, 1),
- ('192.0.2.2', 53), TEST_DATA)
- self.assertRaises(TypeError, self.forwarder.push, 1, AF_INET6,
- SOCK_DGRAM, IPPROTO_UDP, ('2001:db8::1', 53, 0, 1),
- ('192.0.2.2', 53), TEST_DATA)
-
- # Empty data: we reject them at least for now
- self.assertRaises(TypeError, self.forwarder.push, 1, AF_INET,
- SOCK_DGRAM, IPPROTO_UDP, ('192.0.2.1', 53),
- ('192.0.2.2', 53), b'')
-
- # Too big data: we reject them at least for now
- self.assertRaises(TypeError, self.forwarder.push, 1, AF_INET,
- SOCK_DGRAM, IPPROTO_UDP, ('192.0.2.1', 53),
- ('192.0.2.2', 53), b'd' * 65536)
-
- # Close the receptor before push. It will result in SIGPIPE (should be
- # ignored) and EPIPE, which will be converted to SocketSessionError.
- self.listen_sock.close()
- self.assertRaises(SocketSessionError, self.forwarder.push, 1, AF_INET,
- SOCK_DGRAM, IPPROTO_UDP, ('192.0.2.1', 53),
- ('192.0.2.2', 53), TEST_DATA)
-
- def create_socket(self, family, type, protocol, addr, do_listen):
- s = socket.socket(family, type, protocol)
- s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- s.bind(addr)
- if do_listen and protocol == IPPROTO_TCP:
- s.listen(1)
- return s
-
- def check_push_and_pop(self, family, type, protocol, local, remote,
- data, new_connection):
- sock = self.create_socket(family, type, protocol, local, True)
- fwd_fd = sock.fileno()
- if protocol == IPPROTO_TCP:
- client_addr = ('::1', 0, 0, 0) if family == AF_INET6 \
- else ('127.0.0.1', 0)
- client_sock = self.create_socket(family, type, protocol,
- client_addr, False)
- client_sock.setblocking(False)
- try:
- client_sock.connect(local)
- except socket.error:
- pass
- server_sock, _ = sock.accept()
- fwd_fd = server_sock.fileno()
-
- # If a new connection is required, start the "server", have the
- # internal forwarder connect to it, and then internally accept it.
- if new_connection:
- self.start_listen()
- self.forwarder.connect_to_receiver()
- self.accept_sock = self.accept_forwarder()
-
- # Then push one socket session via the forwarder.
- self.forwarder.push(fwd_fd, family, type, protocol, local, remote,
- data)
-
- # Pop the socket session we just pushed from a local receiver, and
- # check the content.
- receiver = SocketSessionReceiver(self.accept_sock)
- signal.alarm(1)
- sock_session = receiver.pop()
- signal.alarm(0)
- passed_sock = sock_session[0]
- self.assertNotEqual(fwd_fd, passed_sock.fileno())
- self.assertEqual(family, passed_sock.family)
- self.assertEqual(type, passed_sock.type)
- self.assertEqual(protocol, passed_sock.proto)
- self.assertEqual(local, sock_session[1])
- self.assertEqual(remote, sock_session[2])
- self.assertEqual(data, sock_session[3])
-
- # Check if the passed FD is usable by sending some data from it.
- passed_sock.setblocking(True)
- if protocol == IPPROTO_UDP:
- self.assertEqual(len(TEST_DATA), passed_sock.sendto(TEST_DATA,
- local))
- sock.settimeout(10)
- self.assertEqual(TEST_DATA, sock.recvfrom(len(TEST_DATA))[0])
- else:
- server_sock.close()
- self.assertEqual(len(TEST_DATA), passed_sock.send(TEST_DATA))
- client_sock.setblocking(True)
- client_sock.settimeout(10)
- self.assertEqual(TEST_DATA, client_sock.recv(len(TEST_DATA)))
-
- def test_push_and_pop(self):
- # This is a straightforward port of C++ pushAndPop test.
- local6 = ('::1', TEST_PORT, 0, 0)
- remote6 = ('2001:db8::1', 5300, 0, 0)
- self.check_push_and_pop(AF_INET6, SOCK_DGRAM, IPPROTO_UDP,
- local6, remote6, TEST_DATA, True)
- self.check_push_and_pop(AF_INET6, SOCK_STREAM, IPPROTO_TCP,
- local6, remote6, TEST_DATA, False)
-
- local4 = ('127.0.0.1', TEST_PORT)
- remote4 = ('192.0.2.2', 5300)
- self.check_push_and_pop(AF_INET, SOCK_DGRAM, IPPROTO_UDP,
- local4, remote4, TEST_DATA, False)
- self.check_push_and_pop(AF_INET, SOCK_STREAM, IPPROTO_TCP,
- local4, remote4, TEST_DATA, False)
-
- self.check_push_and_pop(AF_INET6, SOCK_DGRAM, IPPROTO_UDP,
- local6, remote6, self.large_text, False)
- self.check_push_and_pop(AF_INET6, SOCK_STREAM, IPPROTO_TCP,
- local6, remote6, self.large_text, False)
- self.check_push_and_pop(AF_INET, SOCK_DGRAM, IPPROTO_UDP,
- local4, remote4, self.large_text, False)
- self.check_push_and_pop(AF_INET, SOCK_STREAM, IPPROTO_TCP,
- local4, remote4, self.large_text, False)
-
- # Python specific: check for an IPv6 scoped address with non 0
- # scope (zone) ID
- scope6 = ('fe80::1', TEST_PORT, 0, 1)
- self.check_push_and_pop(AF_INET6, SOCK_DGRAM, IPPROTO_UDP,
- local6, scope6, TEST_DATA, False)
-
- def test_push_too_fast(self):
- # A straightforward port of C++ pushTooFast test.
- def multi_push(forwarder, addr, data):
- for i in range(0, 10):
- forwarder.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP, addr,
- addr, data)
- self.start_listen()
- self.forwarder.connect_to_receiver()
- self.assertRaises(SocketSessionError, multi_push, self.forwarder,
- ('192.0.2.1', 53), self.large_text)
-
- def test_bad_pop(self):
- # This is a subset of C++ badPop test. We only check pop() raises
- # SocketSessionError when it internally fails to get the FD.
- # Other cases would require passing a valid FD from the test,
- # which would make the test too complicated. As a wrapper checking
- # one common failure case should be reasonably sufficient.
-
- self.start_listen()
- s = socket.socket(socket.AF_UNIX, SOCK_STREAM, 0)
- s.setblocking(False)
- s.connect(TEST_UNIX_FILE)
- accept_sock = self.accept_forwarder()
- receiver = SocketSessionReceiver(accept_sock)
- s.close()
- self.assertRaises(SocketSessionError, receiver.pop)
-
-class TestReceiver(unittest.TestCase):
- # We only check a couple of failure cases on construction. Valid cases
- # are covered in TestForwarder.
-
- def test_bad_init(self):
- class FakeSocket:
- # pretending to be th standard socket class, but its fileno() is
- # bogus.
- def fileno(self):
- return None
- self.assertRaises(TypeError, SocketSessionReceiver, 1)
- self.assertRaises(TypeError, SocketSessionReceiver, FakeSocket())
-
-if __name__ == '__main__':
- unittest.main()
More information about the bind10-changes
mailing list