BIND 10 master, updated. 3b6ddc0b213a61abf826d87351d97de9145a3fc4 Changelog
BIND 10 source code commits
bind10-changes at lists.isc.org
Thu Apr 7 10:00:53 UTC 2011
The branch, master has been updated
via 3b6ddc0b213a61abf826d87351d97de9145a3fc4 (commit)
via 28b01ad5bf72472c824a7b8fc4a8dc394e22e462 (commit)
via 745b08afcdce71ca9b50b2d5a7ffe5d4ffa66091 (commit)
via b509cbb77d31e388df68dfe52709d6edef93df3f (commit)
via d12560d1714fb3a07e56b6903141d82d6081148d (commit)
via 20f12e8672c45540cd72bb5df44cf21ff3b6732f (commit)
via 7b2536d7a654ecb24d98c3c0814c7384230e81df (commit)
via a3f3377467d511fb0e789957a4b7a9105e4fe0d2 (commit)
via 13668e95ca6bbb07128babb14f1772bfefcb09a8 (commit)
via eddf905784249ab6949e6186f2676f9d627b089c (commit)
via b4111d2495797a6a9738df6dda3b9db696c5388b (commit)
via 4a484575725500dd766516377eda41daaa17f402 (commit)
via fa743a8d8303433152e7c93bfc8aca1a2aa9cf0e (commit)
via 4434d7bef7a127ad8fb7e9dc6a43c9bd9e6601d1 (commit)
via c7a0fffa73e28d0d28b3706a9bc6b6e1ed9d2a7b (commit)
via 344410fec60755f6b5cb2348ec9e7b67068f31ae (commit)
via e88330e1dbfed90776b634513372f71caa96ac5f (commit)
via 6f9b0595718f8f7fd5324a6252207d29c40836b2 (commit)
via d11a387719a4ea69946801e4c865323f4b9016a3 (commit)
via 88d4219ba95fa853b07c3397aeb4c2e6567e50c0 (commit)
via 8da3cbf0ec4fcd563b8e779038a38811a77dece8 (commit)
via 66bb0f76db68b8f96fc4ac819f9c07c7f8a404e6 (commit)
via 26b876a067e285be526793254f9422067a89e071 (commit)
via be4ec3ec23084ca94e72a60ddb1a2d4250ade5cd (commit)
via d58451bac9dd51a562a24032d013e1eacc5a3fde (commit)
via 6b09fa3b6e19b73f82940664802158baaca0297d (commit)
via 1f7ffbad521f059f611876c27d8373f355ca8c40 (commit)
via 0add6c7b1047219eda3f1d7fa13340da61cd96b5 (commit)
via 3b1fef1e85522e061114beb6bd9013a51da16c08 (commit)
via 3772748aa192829099877ac1c1e43e43b691df16 (commit)
via 7dab055fe9854655b43338485ec8a960dea94080 (commit)
via 78533f09dbf43b91578392dc8cee6700b3db7d2b (commit)
via 5d8d976fe843af90f06dc9ff48408e96a51a0316 (commit)
from 583d7973d8f49c061dee13a14c89cf0846f84721 (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 3b6ddc0b213a61abf826d87351d97de9145a3fc4
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Thu Apr 7 11:47:54 2011 +0200
Changelog
commit 28b01ad5bf72472c824a7b8fc4a8dc394e22e462
Merge: 745b08afcdce71ca9b50b2d5a7ffe5d4ffa66091 7b2536d7a654ecb24d98c3c0814c7384230e81df
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Thu Apr 7 11:46:09 2011 +0200
Merge branch 'work/sockets'
commit 745b08afcdce71ca9b50b2d5a7ffe5d4ffa66091
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Thu Apr 7 11:36:34 2011 +0200
Changelog
commit b509cbb77d31e388df68dfe52709d6edef93df3f
Merge: 583d7973d8f49c061dee13a14c89cf0846f84721 d12560d1714fb3a07e56b6903141d82d6081148d
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Thu Apr 7 11:32:49 2011 +0200
Merge branch 'merge/sockcreator'
commit d12560d1714fb3a07e56b6903141d82d6081148d
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Thu Apr 7 11:32:33 2011 +0200
[trac366] README tweaks
commit 20f12e8672c45540cd72bb5df44cf21ff3b6732f
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Wed Apr 6 22:07:09 2011 +0200
[trac366] Small fixups
commit 7b2536d7a654ecb24d98c3c0814c7384230e81df
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Wed Apr 6 13:02:27 2011 +0200
[trac615] Support setting of xfrout socket
commit a3f3377467d511fb0e789957a4b7a9105e4fe0d2
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Wed Apr 6 12:59:54 2011 +0200
[trac615] Tests for path setup in auth
commit 13668e95ca6bbb07128babb14f1772bfefcb09a8
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Mon Mar 28 11:30:13 2011 +0200
[trac366] Fix tests
It needed very large stack, causing a stack overflow sometimes.
commit eddf905784249ab6949e6186f2676f9d627b089c
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Mon Mar 28 11:01:30 2011 +0200
[trac366] Update old copyrights
commit b4111d2495797a6a9738df6dda3b9db696c5388b
Merge: ece21d1bcb8b6ef6015622427cdc784e951b7396 fa743a8d8303433152e7c93bfc8aca1a2aa9cf0e
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Mon Mar 28 10:52:38 2011 +0200
[trac366] Update from master
Since the branch is years old.
It compiles, but the tests don't run. There's still some work to do.
Conflicts:
configure.ac
doc/Doxyfile
src/bin/Makefile.am
src/lib/Makefile.am
src/lib/util/io/fd_share.h
src/lib/util/io/fdshare_python.cc
src/lib/util/io/tests/run_unittests.cc
commit 4a484575725500dd766516377eda41daaa17f402
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Fri Mar 18 20:16:42 2011 +0100
[trac615] XfrOut can use different socket
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 17 +-
configure.ac | 7 +
doc/Doxyfile | 6 +-
src/bin/Makefile.am | 2 +-
src/bin/auth/Makefile.am | 2 +-
src/{lib/log/dummylog.cc => bin/auth/common.cc} | 37 ++--
src/bin/auth/common.h | 11 +-
src/bin/auth/main.cc | 16 +-
src/bin/auth/tests/Makefile.am | 2 +
src/bin/auth/tests/common_unittest.cc | 96 +++++++
src/bin/sockcreator/Makefile.am | 18 ++
src/bin/sockcreator/README | 49 ++++
.../run_unittests.cc => bin/sockcreator/main.cc} | 16 +-
src/bin/sockcreator/sockcreator.cc | 151 +++++++++++
src/bin/sockcreator/sockcreator.h | 100 ++++++++
src/bin/sockcreator/tests/Makefile.am | 25 ++
.../log => bin/sockcreator}/tests/run_unittests.cc | 5 +-
src/bin/sockcreator/tests/sockcreator_tests.cc | 269 ++++++++++++++++++++
src/bin/xfrout/tests/Makefile.am | 4 +-
.../tests/{xfrout_test.py => xfrout_test.py.in} | 32 +++
src/bin/xfrout/xfrout.py.in | 41 ++--
src/lib/Makefile.am | 2 +-
src/lib/util/Makefile.am | 3 +
src/lib/util/io/Makefile.am | 16 ++
src/lib/util/io/fd.cc | 70 +++++
src/lib/util/io/fd.h | 61 +++++
src/lib/{xfr => util/io}/fd_share.cc | 18 +-
src/lib/util/io/fd_share.h | 65 +++++
src/lib/{xfr => util/io}/fdshare_python.cc | 31 ++-
src/lib/util/io/tests/Makefile.am | 25 ++
src/lib/util/io/tests/fd_share_tests.cc | 74 ++++++
src/lib/util/io/tests/fd_tests.cc | 66 +++++
src/lib/{log => util/io}/tests/run_unittests.cc | 5 +-
src/lib/util/unittests/Makefile.am | 9 +
src/lib/util/unittests/README | 5 +
src/lib/util/unittests/fork.cc | 145 +++++++++++
src/lib/util/unittests/fork.h | 52 ++++
src/lib/xfr/Makefile.am | 14 +-
src/lib/xfr/fd_share.h | 42 ---
src/lib/xfr/python_xfr.cc | 32 ---
src/lib/xfr/xfrout_client.cc | 5 +-
41 files changed, 1472 insertions(+), 174 deletions(-)
copy src/{lib/log/dummylog.cc => bin/auth/common.cc} (51%)
create mode 100644 src/bin/auth/tests/common_unittest.cc
create mode 100644 src/bin/sockcreator/Makefile.am
create mode 100644 src/bin/sockcreator/README
copy src/{lib/server_common/tests/run_unittests.cc => bin/sockcreator/main.cc} (73%)
create mode 100644 src/bin/sockcreator/sockcreator.cc
create mode 100644 src/bin/sockcreator/sockcreator.h
create mode 100644 src/bin/sockcreator/tests/Makefile.am
copy src/{lib/log => bin/sockcreator}/tests/run_unittests.cc (93%)
create mode 100644 src/bin/sockcreator/tests/sockcreator_tests.cc
rename src/bin/xfrout/tests/{xfrout_test.py => xfrout_test.py.in} (93%)
create mode 100644 src/lib/util/Makefile.am
create mode 100644 src/lib/util/io/Makefile.am
create mode 100644 src/lib/util/io/fd.cc
create mode 100644 src/lib/util/io/fd.h
rename src/lib/{xfr => util/io}/fd_share.cc (93%)
create mode 100644 src/lib/util/io/fd_share.h
rename src/lib/{xfr => util/io}/fdshare_python.cc (70%)
create mode 100644 src/lib/util/io/tests/Makefile.am
create mode 100644 src/lib/util/io/tests/fd_share_tests.cc
create mode 100644 src/lib/util/io/tests/fd_tests.cc
copy src/lib/{log => util/io}/tests/run_unittests.cc (93%)
create mode 100644 src/lib/util/unittests/Makefile.am
create mode 100644 src/lib/util/unittests/README
create mode 100644 src/lib/util/unittests/fork.cc
create mode 100644 src/lib/util/unittests/fork.h
delete mode 100644 src/lib/xfr/fd_share.h
delete mode 100644 src/lib/xfr/python_xfr.cc
-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 5ddfe3d..3f0d24d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,9 +1,22 @@
+ 216. [func] vorner
+ The BIND10_XFROUT_SOCKET_FILE environment variable can be used to specify
+ which socket should be used for communication between b10-auth and
+ b10-xfrout. Mostly for testing reasons.
+ (Trac #615, git 28b01ad5bf72472c824a7b8fc4a8dc394e22e462)
+
+ 215. [func] vorner
+ A new process, b10-sockcreator, is added, which will create sockets for
+ the rest of the system. It is the only part which will need to keep the
+ root privileges. However, only the process exists, nothing can talk to it
+ yet.
+ (Trac #366, git b509cbb77d31e388df68dfe52709d6edef93df3f)
+
214. [func]* vorner
Zone manager no longer thinks it is secondary master for
all zones in the database. They are listed in
Zonemgr/secondary_zones configuration variable (in the form
[{"name": "example.com", "class": "IN"}]).
- (Trac #670, 7c1e4d5e1e28e556b1d10a8df8d9486971a3f052)
+ (Trac #670, git 7c1e4d5e1e28e556b1d10a8df8d9486971a3f052)
213. [bug] naokikambe
Solved incorrect datetime of "bind10.boot_time" and also added a new
@@ -22,7 +35,7 @@
211. [func] shane
Implement "--brittle" option, which causes the server to exit
- if any of BIND 10's processes dies.
+ if any of BIND 10's processes dies.
(Trac #788, git 88c0d241fe05e5ea91b10f046f307177cc2f5bc5)
210. [bug] jerry
diff --git a/configure.ac b/configure.ac
index 1c063c4..8e16d3b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -624,6 +624,8 @@ AC_CONFIG_FILES([Makefile
src/bin/auth/benchmarks/Makefile
src/bin/resolver/Makefile
src/bin/resolver/tests/Makefile
+ src/bin/sockcreator/Makefile
+ src/bin/sockcreator/tests/Makefile
src/bin/xfrin/Makefile
src/bin/xfrin/tests/Makefile
src/bin/xfrout/Makefile
@@ -692,6 +694,10 @@ AC_CONFIG_FILES([Makefile
src/lib/server_common/tests/Makefile
tests/Makefile
tests/system/Makefile
+ src/lib/util/Makefile
+ src/lib/util/io/Makefile
+ src/lib/util/io/tests/Makefile
+ src/lib/util/unittests/Makefile
])
AC_OUTPUT([doc/version.ent
src/bin/cfgmgr/b10-cfgmgr.py
@@ -706,6 +712,7 @@ AC_OUTPUT([doc/version.ent
src/bin/xfrout/xfrout.py
src/bin/xfrout/xfrout.spec.pre
src/bin/xfrout/tests/xfrout_test
+ src/bin/xfrout/tests/xfrout_test.py
src/bin/xfrout/run_b10-xfrout.sh
src/bin/resolver/resolver.spec.pre
src/bin/resolver/spec_config.h.pre
diff --git a/doc/Doxyfile b/doc/Doxyfile
index 46aa178..83e85b8 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -568,7 +568,11 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
-INPUT = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions ../src/lib/datasrc ../src/bin/auth ../src/bin/resolver ../src/lib/bench ../src/lib/log ../src/lib/asiolink/ ../src/lib/nsas ../src/lib/testutils ../src/lib/cache ../src/lib/server_common/
+INPUT = ../src/lib/cc ../src/lib/config ../src/lib/dns \
+ ../src/lib/exceptions ../src/lib/datasrc ../src/bin/auth \
+ ../src/bin/resolver ../src/lib/bench ../src/lib/log ../src/lib/asiolink/ \
+ ../src/lib/nsas ../src/lib/testutils ../src/lib/cache \
+ ../src/lib/server_common/ ../src/bin/sockcreator/ ../src/lib/util/
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am
index 1768ce7..23d660c 100644
--- a/src/bin/Makefile.am
+++ b/src/bin/Makefile.am
@@ -1,4 +1,4 @@
SUBDIRS = bind10 bindctl cfgmgr loadzone msgq host cmdctl auth xfrin xfrout \
- usermgr zonemgr stats tests resolver
+ usermgr zonemgr stats tests resolver sockcreator
check-recursive: all-recursive
diff --git a/src/bin/auth/Makefile.am b/src/bin/auth/Makefile.am
index 54dc03c..dfe23ee 100644
--- a/src/bin/auth/Makefile.am
+++ b/src/bin/auth/Makefile.am
@@ -41,7 +41,7 @@ b10_auth_SOURCES += auth_srv.cc auth_srv.h
b10_auth_SOURCES += change_user.cc change_user.h
b10_auth_SOURCES += auth_config.cc auth_config.h
b10_auth_SOURCES += command.cc command.h
-b10_auth_SOURCES += common.h
+b10_auth_SOURCES += common.h common.cc
b10_auth_SOURCES += statistics.cc statistics.h
b10_auth_SOURCES += main.cc
b10_auth_LDADD = $(top_builddir)/src/lib/datasrc/libdatasrc.la
diff --git a/src/bin/auth/common.cc b/src/bin/auth/common.cc
new file mode 100644
index 0000000..fe383e2
--- /dev/null
+++ b/src/bin/auth/common.cc
@@ -0,0 +1,36 @@
+// Copyright (C) 2009-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 <auth/common.h>
+#include <auth/spec_config.h>
+#include <cstdlib>
+
+using std::string;
+
+string getXfroutSocketPath() {
+ if (getenv("B10_FROM_BUILD") != NULL) {
+ if (getenv("B10_FROM_SOURCE_LOCALSTATEDIR")) {
+ return (string(getenv("B10_FROM_SOURCE_LOCALSTATEDIR")) +
+ "/auth_xfrout_conn");
+ } else {
+ return (string(getenv("B10_FROM_BUILD")) + "/auth_xfrout_conn");
+ }
+ } else {
+ if (getenv("BIND10_XFROUT_SOCKET_FILE")) {
+ return (getenv("BIND10_XFROUT_SOCKET_FILE"));
+ } else {
+ return (UNIX_SOCKET_FILE);
+ }
+ }
+}
diff --git a/src/bin/auth/common.h b/src/bin/auth/common.h
index 6af09fb..b913593 100644
--- a/src/bin/auth/common.h
+++ b/src/bin/auth/common.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2009-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
@@ -29,6 +29,15 @@ public:
{}
};
+/// \short Get the path of socket to talk to xfrout
+///
+/// It takes some environment variables into account (B10_FROM_BUILD,
+/// B10_FROM_SOURCE_LOCALSTATEDIR and BIND10_XFROUT_SOCKET_FILE). It
+/// also considers the installation prefix.
+///
+/// The logic should be the same as in b10-xfrout, so they find each other.
+std::string getXfroutSocketPath();
+
#endif // __COMMON_H
// Local Variables:
diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc
index 9743eef..64a8325 100644
--- a/src/bin/auth/main.cc
+++ b/src/bin/auth/main.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2009-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
@@ -122,19 +122,7 @@ main(int argc, char* argv[]) {
bool xfrin_session_established = false; // XXX (see Trac #287)
bool statistics_session_established = false; // XXX (see Trac #287)
ModuleCCSession* config_session = NULL;
- string xfrout_socket_path;
- if (getenv("B10_FROM_BUILD") != NULL) {
- if (getenv("B10_FROM_SOURCE_LOCALSTATEDIR")) {
- xfrout_socket_path = string("B10_FROM_SOURCE_LOCALSTATEDIR") +
- "/auth_xfrout_conn";
- } else {
- xfrout_socket_path = string(getenv("B10_FROM_BUILD")) +
- "/auth_xfrout_conn";
- }
- } else {
- xfrout_socket_path = UNIX_SOCKET_FILE;
- }
- XfroutClient xfrout_client(xfrout_socket_path);
+ XfroutClient xfrout_client(getXfroutSocketPath());
try {
string specfile;
if (getenv("B10_FROM_BUILD")) {
diff --git a/src/bin/auth/tests/Makefile.am b/src/bin/auth/tests/Makefile.am
index 8845755..0e37da5 100644
--- a/src/bin/auth/tests/Makefile.am
+++ b/src/bin/auth/tests/Makefile.am
@@ -24,10 +24,12 @@ run_unittests_SOURCES += ../query.h ../query.cc
run_unittests_SOURCES += ../change_user.h ../change_user.cc
run_unittests_SOURCES += ../auth_config.h ../auth_config.cc
run_unittests_SOURCES += ../command.h ../command.cc
+run_unittests_SOURCES += ../common.h ../common.cc
run_unittests_SOURCES += ../statistics.h ../statistics.cc
run_unittests_SOURCES += auth_srv_unittest.cc
run_unittests_SOURCES += config_unittest.cc
run_unittests_SOURCES += command_unittest.cc
+run_unittests_SOURCES += common_unittest.cc
run_unittests_SOURCES += query_unittest.cc
run_unittests_SOURCES += change_user_unittest.cc
run_unittests_SOURCES += statistics_unittest.cc
diff --git a/src/bin/auth/tests/common_unittest.cc b/src/bin/auth/tests/common_unittest.cc
new file mode 100644
index 0000000..9b18142
--- /dev/null
+++ b/src/bin/auth/tests/common_unittest.cc
@@ -0,0 +1,96 @@
+// 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 <gtest/gtest.h>
+#include <auth/common.h>
+#include <auth/spec_config.h>
+#include <vector>
+#include <string>
+#include <cstdio>
+#include <boost/foreach.hpp>
+
+using std::pair;
+using std::vector;
+using std::string;
+
+namespace {
+
+class Paths : public ::testing::Test {
+private:
+ typedef pair<string, string*> Environ;
+ vector<Environ> restoreEnviron;
+public:
+ void TearDown() {
+ // Restore the original environment
+ BOOST_FOREACH(const Environ &env, restoreEnviron) {
+ if (env.second == NULL) {
+ EXPECT_EQ(0, unsetenv(env.first.c_str())) <<
+ "Couldn't restore environment, results of other tests"
+ "are uncertain";
+ } else {
+ EXPECT_EQ(0, setenv(env.first.c_str(), env.second->c_str(),
+ 1)) << "Couldn't restore environment, "
+ "results of other tests are uncertain";
+ }
+ }
+ }
+protected:
+ // Sets a temporary value into environment. If value is empty, it deletes
+ // the variable from environment (just for simplicity).
+ void setEnv(const string& name, const string& value) {
+ // Backup the original environment
+ char* env(getenv(name.c_str()));
+ restoreEnviron.push_back(Environ(name, env == NULL ? NULL :
+ new string(env)));
+ // Set the new value
+ if (value.empty()) {
+ EXPECT_EQ(0, unsetenv(name.c_str()));
+ } else {
+ EXPECT_EQ(0, setenv(name.c_str(), value.c_str(), 1));
+ }
+ }
+ // Test getXfroutSocketPath under given environment
+ void testXfrout(const string& fromBuild, const string& localStateDir,
+ const string& socketFile, const string& expected)
+ {
+ setEnv("B10_FROM_BUILD", fromBuild);
+ setEnv("B10_FROM_SOURCE_LOCALSTATEDIR", localStateDir);
+ setEnv("BIND10_XFROUT_SOCKET_FILE", socketFile);
+ EXPECT_EQ(expected, getXfroutSocketPath());
+ }
+};
+
+// Test that when we have no special environment, we get the default from prefix
+TEST_F(Paths, xfroutNoEnv) {
+ testXfrout("", "", "", UNIX_SOCKET_FILE);
+}
+
+// Override by B10_FROM_BUILD
+TEST_F(Paths, xfroutFromBuild) {
+ testXfrout("/from/build", "", "/wrong/path",
+ "/from/build/auth_xfrout_conn");
+}
+
+// Override by B10_FROM_SOURCE_LOCALSTATEDIR
+TEST_F(Paths, xfroutLocalStatedir) {
+ testXfrout("/wrong/path", "/state/dir", "/wrong/path",
+ "/state/dir/auth_xfrout_conn");
+}
+
+// Override by BIND10_XFROUT_SOCKET_FILE explicitly
+TEST_F(Paths, xfroutFromEnv) {
+ testXfrout("", "", "/the/path/to/file", "/the/path/to/file");
+}
+
+}
diff --git a/src/bin/sockcreator/Makefile.am b/src/bin/sockcreator/Makefile.am
new file mode 100644
index 0000000..1ac4640
--- /dev/null
+++ b/src/bin/sockcreator/Makefile.am
@@ -0,0 +1,18 @@
+SUBDIRS = tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+
+CLEANFILES = *.gcno *.gcda
+
+pkglibexec_PROGRAMS = b10-sockcreator
+
+b10_sockcreator_SOURCES = sockcreator.cc sockcreator.h main.cc
+b10_sockcreator_LDADD = $(top_builddir)/src/lib/util/io/libutil_io.la
diff --git a/src/bin/sockcreator/README b/src/bin/sockcreator/README
new file mode 100644
index 0000000..4dbbee7
--- /dev/null
+++ b/src/bin/sockcreator/README
@@ -0,0 +1,49 @@
+The socket creator
+==================
+
+The only thing we need higher rights than standard user is binding sockets to
+ports lower than 1024. So we will have a separate process that keeps the
+rights, while the rests drop them for security reasons.
+
+This process is the socket creator. Its goal is to be as simple as possible
+and to contain as little code as possible to minimise the amount of code
+running with higher privileges (to minimize the number of bugs and make
+checking/auditing it easier). It uses low-level OS API instead of some
+fancy library for that reason. It has only fixed-length reads so there's no
+place for buffer overruns.
+
+Protocol
+--------
+
+It talks with whoever started it by its stdin/stdout. It reads simple
+binary protocol from stdin and does what the commands ask. Command is a single
+byte (usually from the printable range, so it is easier to debug and guess
+what it does), followed by parameters.
+
+Note that as send_fd and recv_fd works only with unix domain socket, it's stdio
+must be a socket, not pipe.
+
+* 'T': It has no parameters. It asks the socket creator to terminate.
+
+* 'S' 'U|T' '4|6' port address: Asks it to create a port. First parameter
+ tels the socket type (either UDP or TCP). The second one is address family
+ (either IPv4 or IPv6). Then there's 2 bytes of the port number, in the
+ network byte order. The last one is either 4 or 16 bytes of address, as
+ they would be passed to bind (note that both parameters are already prepared,
+ like hton called on them).
+
+ The answer to this is either 'S' directly followed by the socket (using
+ sendmsg) if it is successful. If it fails, 'E' is returned instead, followed
+ by either 'S' or 'B' (either socket() or bind() call failed). Then there is
+ one int (architecture-dependent length and endianess), which is the errno
+ value after the failure.
+
+The creator may also send these messages at any time (but not in the middle
+of another message):
+
+* 'F': A fatal error has been detected. It is followed by one byte of error
+ condition code and then the creator terminates with non-zero status.
+
+ The conditions are:
+ * 'I': Invalid input (eg. someone sent a wrong letter and it does not
+ understand it).
diff --git a/src/bin/sockcreator/main.cc b/src/bin/sockcreator/main.cc
new file mode 100644
index 0000000..37da303
--- /dev/null
+++ b/src/bin/sockcreator/main.cc
@@ -0,0 +1,26 @@
+// 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 "sockcreator.h"
+
+using namespace isc::socket_creator;
+
+int
+main() {
+ /*
+ * TODO Maybe use some OS-specific caps interface and drop everything
+ * but ability to bind ports? It would be nice.
+ */
+ return run(0, 1); // Read commands from stdin, output to stdout
+}
diff --git a/src/bin/sockcreator/sockcreator.cc b/src/bin/sockcreator/sockcreator.cc
new file mode 100644
index 0000000..b9da84f
--- /dev/null
+++ b/src/bin/sockcreator/sockcreator.cc
@@ -0,0 +1,151 @@
+// 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 "sockcreator.h"
+
+#include <util/io/fd.h>
+
+#include <unistd.h>
+#include <cerrno>
+#include <cstring>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+using namespace isc::util::io;
+
+namespace isc {
+namespace socket_creator {
+
+int
+get_sock(const int type, struct sockaddr *bind_addr, const socklen_t addr_len)
+{
+ int sock(socket(bind_addr->sa_family, type, 0));
+ if (sock == -1) {
+ return -1;
+ }
+ if (bind(sock, bind_addr, addr_len) == -1) {
+ return -2;
+ }
+ return sock;
+}
+
+// These are macros so they can exit the function
+#define READ(WHERE, HOW_MANY) do { \
+ size_t how_many = (HOW_MANY); \
+ if (read_data(input_fd, (WHERE), how_many) < how_many) { \
+ return 1; \
+ } \
+ } while (0)
+
+#define WRITE(WHAT, HOW_MANY) do { \
+ if (!write_data(output_fd, (WHAT), (HOW_MANY))) { \
+ return 2; \
+ } \
+ } while (0)
+
+#define DEFAULT \
+ default: /* Unrecognized part of protocol */ \
+ WRITE("FI", 2); \
+ return 3;
+
+int
+run(const int input_fd, const int output_fd, const get_sock_t get_sock,
+ const send_fd_t send_fd)
+{
+ for (;;) {
+ // Read the command
+ char command;
+ READ(&command, 1);
+ switch (command) {
+ case 'T': // The "terminate" command
+ return 0;
+ case 'S': { // Create a socket
+ // Read what type of socket they want
+ char type[2];
+ READ(type, 2);
+ // Read the address they ask for
+ struct sockaddr *addr(NULL);
+ size_t addr_len(0);
+ struct sockaddr_in addr_in;
+ struct sockaddr_in6 addr_in6;
+ switch (type[1]) { // The address family
+ /*
+ * Here are some casts. They are required by C++ and
+ * the low-level interface (they are implicit in C).
+ */
+ case '4':
+ addr = static_cast<struct sockaddr *>(
+ static_cast<void *>(&addr_in));
+ addr_len = sizeof addr_in;
+ memset(&addr_in, 0, sizeof addr_in);
+ addr_in.sin_family = AF_INET;
+ READ(static_cast<char *>(static_cast<void *>(
+ &addr_in.sin_port)), 2);
+ READ(static_cast<char *>(static_cast<void *>(
+ &addr_in.sin_addr.s_addr)), 4);
+ break;
+ case '6':
+ addr = static_cast<struct sockaddr *>(
+ static_cast<void *>(&addr_in6));
+ addr_len = sizeof addr_in6;
+ memset(&addr_in6, 0, sizeof addr_in6);
+ addr_in6.sin6_family = AF_INET6;
+ READ(static_cast<char *>(static_cast<void *>(
+ &addr_in6.sin6_port)), 2);
+ READ(static_cast<char *>(static_cast<void *>(
+ &addr_in6.sin6_addr.s6_addr)), 16);
+ break;
+ DEFAULT
+ }
+ int sock_type;
+ switch (type[0]) { // Translate the type
+ case 'T':
+ sock_type = SOCK_STREAM;
+ break;
+ case 'U':
+ sock_type = SOCK_DGRAM;
+ break;
+ DEFAULT
+ }
+ int result(get_sock(sock_type, addr, addr_len));
+ if (result >= 0) { // We got the socket
+ WRITE("S", 1);
+ // FIXME: Check the output and write a test for it
+ send_fd(output_fd, result);
+ } else {
+ WRITE("E", 1);
+ switch (result) {
+ case -1:
+ WRITE("S", 1);
+ break;
+ case -2:
+ WRITE("B", 1);
+ break;
+ default:
+ return 4;
+ }
+ int error(errno);
+ WRITE(static_cast<char *>(static_cast<void *>(&error)),
+ sizeof error);
+ }
+ break;
+ }
+ DEFAULT
+ }
+ }
+}
+
+} // End of the namespaces
+}
diff --git a/src/bin/sockcreator/sockcreator.h b/src/bin/sockcreator/sockcreator.h
new file mode 100644
index 0000000..ddf9a09
--- /dev/null
+++ b/src/bin/sockcreator/sockcreator.h
@@ -0,0 +1,100 @@
+// 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.
+
+/**
+ * \file sockcreator.h
+ * \short Socket creator functionality.
+ *
+ * This module holds the functionality of the socket creator. It is
+ * a separate module from main to ease up the tests.
+ */
+
+#ifndef __SOCKCREATOR_H
+#define __SOCKCREATOR_H 1
+
+#include <util/io/fd_share.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+namespace isc {
+namespace socket_creator {
+
+/**
+ * \short Create a socket and bind it.
+ *
+ * This is just a bundle of socket() and bind() calls. The sa_family of
+ * bind_addr is used to determine the domain of the socket.
+ *
+ * \return The file descriptor of the newly created socket, if everything
+ * goes well. A negative number is returned if an error occurs -
+ * -1 if the socket() call fails or -2 if bind() fails. In case of error,
+ * errno is set (or better, left intact from socket() or bind()).
+ * \param type The type of socket to create (SOCK_STREAM, SOCK_DGRAM, etc).
+ * \param bind_addr The address to bind.
+ * \param addr_len The actual length of bind_addr.
+ */
+int
+get_sock(const int type, struct sockaddr *bind_addr, const socklen_t addr_len);
+
+/**
+ * Type of the get_sock function, to pass it as parameter.
+ */
+typedef
+int
+(*get_sock_t)(const int, struct sockaddr *, const socklen_t);
+
+/**
+ * Type of the send_fd() function, so it can be passed as a parameter.
+ */
+typedef
+int
+(*send_fd_t)(const int, const int);
+
+/**
+ * \short Infinite loop parsing commands and returning the sockets.
+ *
+ * This reads commands and socket descriptions from the input_fd
+ * file descriptor, creates sockets and writes the results (socket or
+ * error) to output_fd.
+ *
+ * Current errors are:
+ * - 1: Read error
+ * - 2: Write error
+ * - 3: Protocol error (unknown command, etc)
+ * - 4: Some internal inconsistency detected
+ *
+ * It terminates either if a command asks it to or when unrecoverable
+ * error happens.
+ *
+ * \return Like a return value of a main - 0 means everything OK, anything
+ * else is error.
+ * \param input_fd Here is where it reads the commads.
+ * \param output_fd Here is where it writes the results.
+ * \param get_sock_fun The function that is used to create the sockets.
+ * This should be left on the default value, the parameter is here
+ * for testing purposes.
+ * \param send_fd_fun The function that is used to send the socket over
+ * a file descriptor. This should be left on the default value, it is
+ * here for testing purposes.
+ */
+int
+run(const int input_fd, const int output_fd,
+ const get_sock_t get_sock_fun = get_sock,
+ const send_fd_t send_fd_fun = isc::util::io::send_fd);
+
+} // End of the namespaces
+}
+
+#endif // __SOCKCREATOR_H
diff --git a/src/bin/sockcreator/tests/Makefile.am b/src/bin/sockcreator/tests/Makefile.am
new file mode 100644
index 0000000..2e1307a
--- /dev/null
+++ b/src/bin/sockcreator/tests/Makefile.am
@@ -0,0 +1,25 @@
+CLEANFILES = *.gcno *.gcda
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES = ../sockcreator.cc ../sockcreator.h
+run_unittests_SOURCES += sockcreator_tests.cc
+run_unittests_SOURCES += run_unittests.cc
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD += $(top_builddir)/src/lib/util/io/libutil_io.la
+run_unittests_LDADD += \
+ $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/sockcreator/tests/run_unittests.cc b/src/bin/sockcreator/tests/run_unittests.cc
new file mode 100644
index 0000000..e787ab1
--- /dev/null
+++ b/src/bin/sockcreator/tests/run_unittests.cc
@@ -0,0 +1,22 @@
+// 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 <gtest/gtest.h>
+
+int
+main(int argc, char *argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ return RUN_ALL_TESTS();
+}
diff --git a/src/bin/sockcreator/tests/sockcreator_tests.cc b/src/bin/sockcreator/tests/sockcreator_tests.cc
new file mode 100644
index 0000000..cfdaa8d
--- /dev/null
+++ b/src/bin/sockcreator/tests/sockcreator_tests.cc
@@ -0,0 +1,269 @@
+// 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 "../sockcreator.h"
+
+#include <util/unittests/fork.h>
+#include <util/io/fd.h>
+
+#include <gtest/gtest.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <cstring>
+#include <cerrno>
+
+using namespace isc::socket_creator;
+using namespace isc::util::unittests;
+using namespace isc::util::io;
+
+namespace {
+
+/*
+ * Generic version of the creation of socket test. It just tries to
+ * create the socket and checks the result is not negative (eg.
+ * it is valid descriptor) and that it can listen.
+ *
+ * This is a macro so ASSERT_* does abort the TEST, not just the
+ * function inside.
+ */
+#define TEST_ANY_CREATE(SOCK_TYPE, ADDR_TYPE, ADDR_FAMILY, FAMILY_FIELD, \
+ ADDR_SET, CHECK_SOCK) \
+ do { \
+ /*
+ * This should create an address that binds on all interfaces
+ * and lets the OS choose a free port.
+ */ \
+ struct ADDR_TYPE addr; \
+ memset(&addr, 0, sizeof addr); \
+ ADDR_SET(addr); \
+ addr.FAMILY_FIELD = ADDR_FAMILY; \
+ struct sockaddr *addr_ptr = static_cast<struct sockaddr *>( \
+ static_cast<void *>(&addr)); \
+ \
+ int socket = get_sock(SOCK_TYPE, addr_ptr, sizeof addr); \
+ /* Provide even nice error message. */ \
+ ASSERT_GE(socket, 0) << "Couldn't create a socket of type " \
+ #SOCK_TYPE " and family " #ADDR_FAMILY ", failed with " \
+ << socket << " and errno " << errno; \
+ CHECK_SOCK(ADDR_TYPE, socket); \
+ EXPECT_EQ(0, close(socket)); \
+ } while (0)
+
+// Just helper macros
+#define INADDR_SET(WHAT) do { WHAT.sin_addr.s_addr = INADDR_ANY; } while (0)
+#define IN6ADDR_SET(WHAT) do { WHAT.sin6_addr = in6addr_any; } while (0)
+// If the get_sock returned something useful, listen must work
+#define TCP_CHECK(UNUSED, SOCKET) do { \
+ EXPECT_EQ(0, listen(SOCKET, 1)); \
+ } while (0)
+// More complicated with UDP, so we send a packet to ourselfs and se if it
+// arrives
+#define UDP_CHECK(ADDR_TYPE, SOCKET) do { \
+ struct ADDR_TYPE addr; \
+ memset(&addr, 0, sizeof addr); \
+ struct sockaddr *addr_ptr = static_cast<struct sockaddr *>( \
+ static_cast<void *>(&addr)); \
+ \
+ socklen_t len = sizeof addr; \
+ ASSERT_EQ(0, getsockname(SOCKET, addr_ptr, &len)); \
+ ASSERT_EQ(5, sendto(SOCKET, "test", 5, 0, addr_ptr, sizeof addr)); \
+ char buffer[5]; \
+ ASSERT_EQ(5, recv(SOCKET, buffer, 5, 0)); \
+ EXPECT_STREQ("test", buffer); \
+ } while (0)
+
+/*
+ * Several tests to ensure we can create the sockets.
+ */
+TEST(get_sock, udp4_create) {
+ TEST_ANY_CREATE(SOCK_DGRAM, sockaddr_in, AF_INET, sin_family, INADDR_SET,
+ UDP_CHECK);
+}
+
+TEST(get_sock, tcp4_create) {
+ TEST_ANY_CREATE(SOCK_STREAM, sockaddr_in, AF_INET, sin_family, INADDR_SET,
+ TCP_CHECK);
+}
+
+TEST(get_sock, udp6_create) {
+ TEST_ANY_CREATE(SOCK_DGRAM, sockaddr_in6, AF_INET6, sin6_family,
+ IN6ADDR_SET, UDP_CHECK);
+}
+
+TEST(get_sock, tcp6_create) {
+ TEST_ANY_CREATE(SOCK_STREAM, sockaddr_in6, AF_INET6, sin6_family,
+ IN6ADDR_SET, TCP_CHECK);
+}
+
+/*
+ * Try to ask the get_sock function some nonsense and test if it
+ * is able to report error.
+ */
+TEST(get_sock, fail_with_nonsense) {
+ struct sockaddr addr;
+ memset(&addr, 0, sizeof addr);
+ ASSERT_LT(get_sock(0, &addr, sizeof addr), 0);
+}
+
+/*
+ * Helper functions to pass to run during testing.
+ */
+int
+get_sock_dummy(const int type, struct sockaddr *addr, const socklen_t)
+{
+ int result(0);
+ int port(0);
+ /*
+ * We encode the type and address family into the int and return it.
+ * Lets ignore the port and address for now
+ * First bit is 1 if it is known type. Second tells if TCP or UDP.
+ * The familly is similar - third bit is known address family,
+ * the fourth is the family.
+ */
+ switch (type) {
+ case SOCK_STREAM:
+ result += 1;
+ break;
+ case SOCK_DGRAM:
+ result += 3;
+ break;
+ }
+ switch (addr->sa_family) {
+ case AF_INET:
+ result += 4;
+ port = static_cast<struct sockaddr_in *>(
+ static_cast<void *>(addr))->sin_port;
+ break;
+ case AF_INET6:
+ result += 12;
+ port = static_cast<struct sockaddr_in6 *>(
+ static_cast<void *>(addr))->sin6_port;
+ break;
+ }
+ /*
+ * The port should be 0xffff. If it's not, we change the result.
+ * The port of 0xbbbb means bind should fail and 0xcccc means
+ * socket should fail.
+ */
+ if (port != 0xffff) {
+ errno = 0;
+ if (port == 0xbbbb) {
+ return -2;
+ } else if (port == 0xcccc) {
+ return -1;
+ } else {
+ result += 16;
+ }
+ }
+ return result;
+}
+
+int
+send_fd_dummy(const int destination, const int what)
+{
+ /*
+ * Make sure it is 1 byte so we know the length. We do not use more during
+ * the test anyway.
+ */
+ char fd_data(what);
+ if (!write_data(destination, &fd_data, 1)) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+/*
+ * Generic test that it works, with various inputs and outputs.
+ * It uses different functions to create the socket and send it and pass
+ * data to it and check it returns correct data back, to see if the run()
+ * parses the commands correctly.
+ */
+void run_test(const char *input_data, const size_t input_size,
+ const char *output_data, const size_t output_size,
+ bool should_succeed = true)
+{
+ // Prepare the input feeder and output checker processes
+ int input_fd(0), output_fd(0);
+ pid_t input(provide_input(&input_fd, input_data, input_size)),
+ output(check_output(&output_fd, output_data, output_size));
+ ASSERT_NE(-1, input) << "Couldn't start input feeder";
+ ASSERT_NE(-1, output) << "Couldn't start output checker";
+ // Run the body
+ int result(run(input_fd, output_fd, get_sock_dummy, send_fd_dummy));
+ // Close the pipes
+ close(input_fd);
+ close(output_fd);
+ // Did it run well?
+ if (should_succeed) {
+ EXPECT_EQ(0, result);
+ } else {
+ EXPECT_NE(0, result);
+ }
+ // Check the subprocesses say everything is OK too
+ EXPECT_TRUE(process_ok(input));
+ EXPECT_TRUE(process_ok(output));
+}
+
+/*
+ * Check it terminates successfully when asked to.
+ */
+TEST(run, terminate) {
+ run_test("T", 1, NULL, 0);
+}
+
+/*
+ * Check it rejects incorrect input.
+ */
+TEST(run, bad_input) {
+ run_test("XXX", 3, "FI", 2, false);
+}
+
+/*
+ * Check it correctly parses queries to create sockets.
+ */
+TEST(run, sockets) {
+ run_test(
+ "SU4\xff\xff\0\0\0\0" // This has 9 bytes
+ "ST4\xff\xff\0\0\0\0" // This has 9 bytes
+ "ST6\xff\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // This has 21 bytes
+ "SU6\xff\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // This has 21 bytes
+ "T", 61,
+ "S\x07S\x05S\x0dS\x0f", 8);
+}
+
+/*
+ * Check if failures of get_socket are handled correctly.
+ */
+TEST(run, bad_sockets) {
+ // We need to construct the answer, but it depends on int length.
+ size_t int_len(sizeof(int));
+ size_t result_len(4 + 2 * int_len);
+ char result[result_len];
+ // Both errno parts should be 0
+ memset(result, 0, result_len);
+ // Fill the 2 control parts
+ strcpy(result, "EB");
+ strcpy(result + 2 + int_len, "ES");
+ // Run the test
+ run_test(
+ "SU4\xbb\xbb\0\0\0\0"
+ "SU4\xcc\xcc\0\0\0\0"
+ "T", 19,
+ result, result_len);
+}
+
+}
diff --git a/src/bin/xfrout/tests/Makefile.am b/src/bin/xfrout/tests/Makefile.am
index 01f2e40..76c5977 100644
--- a/src/bin/xfrout/tests/Makefile.am
+++ b/src/bin/xfrout/tests/Makefile.am
@@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS)
# required by loadable python modules.
LIBRARY_PATH_PLACEHOLDER =
if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/xfr/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH)
endif
# test using command-line arguments, so use check-local target instead of TESTS
@@ -18,7 +18,7 @@ if ENABLE_PYTHON_COVERAGE
endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
- env PYTHONPATH=$(abs_top_builddir)/src/bin/xfrout:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/xfr/.libs \
+ env PYTHONPATH=$(abs_top_builddir)/src/bin/xfrout:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/util/io/.libs \
$(LIBRARY_PATH_PLACEHOLDER) \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
diff --git a/src/bin/xfrout/tests/xfrout_test.py b/src/bin/xfrout/tests/xfrout_test.py
deleted file mode 100644
index 5aec072..0000000
--- a/src/bin/xfrout/tests/xfrout_test.py
+++ /dev/null
@@ -1,437 +0,0 @@
-# Copyright (C) 2010 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.
-
-'''Tests for the XfroutSession and UnixSockServer classes '''
-
-
-import unittest
-import os
-from isc.cc.session import *
-from pydnspp import *
-from xfrout import *
-
-# our fake socket, where we can read and insert messages
-class MySocket():
- def __init__(self, family, type):
- self.family = family
- self.type = type
- self.sendqueue = bytearray()
-
- def connect(self, to):
- pass
-
- def close(self):
- pass
-
- def send(self, data):
- self.sendqueue.extend(data);
- return len(data)
-
- def readsent(self):
- if len(self.sendqueue) >= 2:
- size = 2 + struct.unpack("!H", self.sendqueue[:2])[0]
- else:
- size = 0
- result = self.sendqueue[:size]
- self.sendqueue = self.sendqueue[size:]
- return result
-
- def read_msg(self):
- sent_data = self.readsent()
- get_msg = Message(Message.PARSE)
- get_msg.from_wire(bytes(sent_data[2:]))
- return get_msg
-
- def clear_send(self):
- del self.sendqueue[:]
-
-# We subclass the Session class we're testing here, only
-# to override the handle() and _send_data() method
-class MyXfroutSession(XfroutSession):
- def handle(self):
- pass
-
- def _send_data(self, sock, data):
- size = len(data)
- total_count = 0
- while total_count < size:
- count = sock.send(data[total_count:])
- total_count += count
-
-class Dbserver:
- def __init__(self):
- self._shutdown_event = threading.Event()
- def get_db_file(self):
- return None
- def decrease_transfers_counter(self):
- pass
-
-class TestXfroutSession(unittest.TestCase):
- def getmsg(self):
- msg = Message(Message.PARSE)
- msg.from_wire(self.mdata)
- return msg
-
- def setUp(self):
- self.sock = MySocket(socket.AF_INET,socket.SOCK_STREAM)
- self.log = isc.log.NSLogger('xfrout', '', severity = 'critical', log_to_console = False )
- self.xfrsess = MyXfroutSession(self.sock, None, Dbserver(), self.log)
- self.mdata = bytes(b'\xd6=\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\xfc\x00\x01')
- self.soa_record = (4, 3, 'example.com.', 'com.example.', 3600, 'SOA', None, 'master.example.com. admin.example.com. 1234 3600 1800 2419200 7200')
-
- def test_parse_query_message(self):
- [get_rcode, get_msg] = self.xfrsess._parse_query_message(self.mdata)
- self.assertEqual(get_rcode.to_text(), "NOERROR")
-
- def test_get_query_zone_name(self):
- msg = self.getmsg()
- self.assertEqual(self.xfrsess._get_query_zone_name(msg), "example.com.")
-
- def test_send_data(self):
- self.xfrsess._send_data(self.sock, self.mdata)
- senddata = self.sock.readsent()
- self.assertEqual(senddata, self.mdata)
-
- def test_reply_xfrout_query_with_error_rcode(self):
- msg = self.getmsg()
- self.xfrsess._reply_query_with_error_rcode(msg, self.sock, Rcode(3))
- get_msg = self.sock.read_msg()
- self.assertEqual(get_msg.get_rcode().to_text(), "NXDOMAIN")
-
- def test_send_message(self):
- msg = self.getmsg()
- msg.make_response()
- # soa record data with different cases
- soa_record = (4, 3, 'Example.com.', 'com.Example.', 3600, 'SOA', None, 'master.Example.com. admin.exAmple.com. 1234 3600 1800 2419200 7200')
- rrset_soa = self.xfrsess._create_rrset_from_db_record(soa_record)
- msg.add_rrset(Message.SECTION_ANSWER, rrset_soa)
- self.xfrsess._send_message(self.sock, msg)
- send_out_data = self.sock.readsent()[2:]
-
- # CASE_INSENSITIVE compression mode
- render = MessageRenderer();
- render.set_length_limit(XFROUT_MAX_MESSAGE_SIZE)
- msg.to_wire(render)
- self.assertNotEqual(render.get_data(), send_out_data)
-
- # CASE_SENSITIVE compression mode
- render.clear()
- render.set_compress_mode(MessageRenderer.CASE_SENSITIVE)
- render.set_length_limit(XFROUT_MAX_MESSAGE_SIZE)
- msg.to_wire(render)
- self.assertEqual(render.get_data(), send_out_data)
-
- def test_clear_message(self):
- msg = self.getmsg()
- qid = msg.get_qid()
- opcode = msg.get_opcode()
- rcode = msg.get_rcode()
-
- self.xfrsess._clear_message(msg)
- self.assertEqual(msg.get_qid(), qid)
- self.assertEqual(msg.get_opcode(), opcode)
- self.assertEqual(msg.get_rcode(), rcode)
- self.assertTrue(msg.get_header_flag(Message.HEADERFLAG_AA))
-
- def test_reply_query_with_format_error(self):
- msg = self.getmsg()
- self.xfrsess._reply_query_with_format_error(msg, self.sock)
- get_msg = self.sock.read_msg()
- self.assertEqual(get_msg.get_rcode().to_text(), "FORMERR")
-
- def test_create_rrset_from_db_record(self):
- rrset = self.xfrsess._create_rrset_from_db_record(self.soa_record)
- self.assertEqual(rrset.get_name().to_text(), "example.com.")
- self.assertEqual(rrset.get_class(), RRClass("IN"))
- self.assertEqual(rrset.get_type().to_text(), "SOA")
- rdata = rrset.get_rdata()
- self.assertEqual(rdata[0].to_text(), self.soa_record[7])
-
- def test_send_message_with_last_soa(self):
- rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
-
- msg = self.getmsg()
- msg.make_response()
- self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa, 0)
- get_msg = self.sock.read_msg()
-
- self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 1)
- self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
- self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
-
- #answer_rrset_iter = section_iter(get_msg, section.ANSWER())
- answer = get_msg.get_section(Message.SECTION_ANSWER)[0]#answer_rrset_iter.get_rrset()
- self.assertEqual(answer.get_name().to_text(), "example.com.")
- self.assertEqual(answer.get_class(), RRClass("IN"))
- self.assertEqual(answer.get_type().to_text(), "SOA")
- rdata = answer.get_rdata()
- self.assertEqual(rdata[0].to_text(), self.soa_record[7])
-
- def test_trigger_send_message_with_last_soa(self):
- rrset_a = RRset(Name("example.com"), RRClass.IN(), RRType.A(), RRTTL(3600))
- rrset_a.add_rdata(Rdata(RRType.A(), RRClass.IN(), "192.0.2.1"))
- rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
-
- msg = self.getmsg()
- msg.make_response()
-
- msg.add_rrset(Message.SECTION_ANSWER, rrset_a)
- # give the function a value that is larger than MAX-len(rrset)
- self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa, 65520)
-
- # this should have triggered the sending of two messages
- # (1 with the rrset we added manually, and 1 that triggered
- # the sending in _with_last_soa)
- get_msg = self.sock.read_msg()
- self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 1)
- self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
- self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
-
- answer = get_msg.get_section(Message.SECTION_ANSWER)[0]
- self.assertEqual(answer.get_name().to_text(), "example.com.")
- self.assertEqual(answer.get_class(), RRClass("IN"))
- self.assertEqual(answer.get_type().to_text(), "A")
- rdata = answer.get_rdata()
- self.assertEqual(rdata[0].to_text(), "192.0.2.1")
-
- get_msg = self.sock.read_msg()
- self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 0)
- self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
- self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
-
- #answer_rrset_iter = section_iter(get_msg, Message.SECTION_ANSWER)
- answer = get_msg.get_section(Message.SECTION_ANSWER)[0]
- self.assertEqual(answer.get_name().to_text(), "example.com.")
- self.assertEqual(answer.get_class(), RRClass("IN"))
- self.assertEqual(answer.get_type().to_text(), "SOA")
- rdata = answer.get_rdata()
- self.assertEqual(rdata[0].to_text(), self.soa_record[7])
-
- # and it should not have sent anything else
- self.assertEqual(0, len(self.sock.sendqueue))
-
- def test_get_rrset_len(self):
- rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
- self.assertEqual(82, get_rrset_len(rrset_soa))
-
- def test_zone_has_soa(self):
- global sqlite3_ds
- def mydb1(zone, file):
- return True
- sqlite3_ds.get_zone_soa = mydb1
- self.assertTrue(self.xfrsess._zone_has_soa(""))
- def mydb2(zone, file):
- return False
- sqlite3_ds.get_zone_soa = mydb2
- self.assertFalse(self.xfrsess._zone_has_soa(""))
-
- def test_zone_exist(self):
- global sqlite3_ds
- def zone_exist(zone, file):
- return zone
- sqlite3_ds.zone_exist = zone_exist
- self.assertTrue(self.xfrsess._zone_exist(True))
- self.assertFalse(self.xfrsess._zone_exist(False))
-
- def test_check_xfrout_available(self):
- def zone_exist(zone):
- return zone
- def zone_has_soa(zone):
- return (not zone)
- self.xfrsess._zone_exist = zone_exist
- self.xfrsess._zone_has_soa = zone_has_soa
- self.assertEqual(self.xfrsess._check_xfrout_available(False).to_text(), "NOTAUTH")
- self.assertEqual(self.xfrsess._check_xfrout_available(True).to_text(), "SERVFAIL")
-
- def zone_empty(zone):
- return zone
- self.xfrsess._zone_has_soa = zone_empty
- def false_func():
- return False
- self.xfrsess._server.increase_transfers_counter = false_func
- self.assertEqual(self.xfrsess._check_xfrout_available(True).to_text(), "REFUSED")
- def true_func():
- return True
- self.xfrsess._server.increase_transfers_counter = true_func
- self.assertEqual(self.xfrsess._check_xfrout_available(True).to_text(), "NOERROR")
-
- def test_dns_xfrout_start_formerror(self):
- # formerror
- self.xfrsess.dns_xfrout_start(self.sock, b"\xd6=\x00\x00\x00\x01\x00")
- sent_data = self.sock.readsent()
- self.assertEqual(len(sent_data), 0)
-
- def default(self, param):
- return "example.com"
-
- def test_dns_xfrout_start_notauth(self):
- self.xfrsess._get_query_zone_name = self.default
- def notauth(formpara):
- return Rcode.NOTAUTH()
- self.xfrsess._check_xfrout_available = notauth
- self.xfrsess.dns_xfrout_start(self.sock, self.mdata)
- get_msg = self.sock.read_msg()
- self.assertEqual(get_msg.get_rcode().to_text(), "NOTAUTH")
-
- def test_dns_xfrout_start_noerror(self):
- self.xfrsess._get_query_zone_name = self.default
- def noerror(form):
- return Rcode.NOERROR()
- self.xfrsess._check_xfrout_available = noerror
-
- def myreply(msg, sock, zonename):
- self.sock.send(b"success")
-
- self.xfrsess._reply_xfrout_query = myreply
- self.xfrsess.dns_xfrout_start(self.sock, self.mdata)
- self.assertEqual(self.sock.readsent(), b"success")
-
- def test_reply_xfrout_query_noerror(self):
- global sqlite3_ds
- def get_zone_soa(zonename, file):
- return self.soa_record
-
- def get_zone_datas(zone, file):
- return [self.soa_record]
-
- sqlite3_ds.get_zone_soa = get_zone_soa
- sqlite3_ds.get_zone_datas = get_zone_datas
- self.xfrsess._reply_xfrout_query(self.getmsg(), self.sock, "example.com.")
- reply_msg = self.sock.read_msg()
- self.assertEqual(reply_msg.get_rr_count(Message.SECTION_ANSWER), 2)
-
-class MyCCSession():
- def __init__(self):
- pass
-
- def get_remote_config_value(self, module_name, identifier):
- if module_name == "Auth" and identifier == "database_file":
- return "initdb.file", False
- else:
- return "unknown", False
-
-
-class MyUnixSockServer(UnixSockServer):
- def __init__(self):
- self._lock = threading.Lock()
- self._transfers_counter = 0
- self._shutdown_event = threading.Event()
- self._max_transfers_out = 10
- self._cc = MyCCSession()
- self._log = isc.log.NSLogger('xfrout', '', severity = 'critical', log_to_console = False )
-
-class TestUnixSockServer(unittest.TestCase):
- def setUp(self):
- self.write_sock, self.read_sock = socket.socketpair()
- self.unix = MyUnixSockServer()
-
- def test_receive_query_message(self):
- send_msg = b"\xd6=\x00\x00\x00\x01\x00"
- msg_len = struct.pack('H', socket.htons(len(send_msg)))
- self.write_sock.send(msg_len)
- self.write_sock.send(send_msg)
- recv_msg = self.unix._receive_query_message(self.read_sock)
- self.assertEqual(recv_msg, send_msg)
-
- def test_updata_config_data(self):
- self.unix.update_config_data({'transfers_out':10 })
- self.assertEqual(self.unix._max_transfers_out, 10)
-
- def test_get_db_file(self):
- self.assertEqual(self.unix.get_db_file(), "initdb.file")
-
- def test_increase_transfers_counter(self):
- self.unix._max_transfers_out = 10
- count = self.unix._transfers_counter
- self.assertEqual(self.unix.increase_transfers_counter(), True)
- self.assertEqual(count + 1, self.unix._transfers_counter)
-
- self.unix._max_transfers_out = 0
- count = self.unix._transfers_counter
- self.assertEqual(self.unix.increase_transfers_counter(), False)
- self.assertEqual(count, self.unix._transfers_counter)
-
- def test_decrease_transfers_counter(self):
- count = self.unix._transfers_counter
- self.unix.decrease_transfers_counter()
- self.assertEqual(count - 1, self.unix._transfers_counter)
-
- def _remove_file(self, sock_file):
- try:
- os.remove(sock_file)
- except OSError:
- pass
-
- def test_sock_file_in_use_file_exist(self):
- sock_file = 'temp.sock.file'
- self._remove_file(sock_file)
- self.assertFalse(self.unix._sock_file_in_use(sock_file))
- self.assertFalse(os.path.exists(sock_file))
-
- def test_sock_file_in_use_file_not_exist(self):
- self.assertFalse(self.unix._sock_file_in_use('temp.sock.file'))
-
- def _start_unix_sock_server(self, sock_file):
- serv = ThreadingUnixStreamServer(sock_file, BaseRequestHandler)
- serv_thread = threading.Thread(target=serv.serve_forever)
- serv_thread.setDaemon(True)
- serv_thread.start()
-
- def test_sock_file_in_use(self):
- sock_file = 'temp.sock.file'
- self._remove_file(sock_file)
- self.assertFalse(self.unix._sock_file_in_use(sock_file))
- self._start_unix_sock_server(sock_file)
-
- old_stdout = sys.stdout
- sys.stdout = open(os.devnull, 'w')
- self.assertTrue(self.unix._sock_file_in_use(sock_file))
- sys.stdout = old_stdout
-
- def test_remove_unused_sock_file_in_use(self):
- sock_file = 'temp.sock.file'
- self._remove_file(sock_file)
- self.assertFalse(self.unix._sock_file_in_use(sock_file))
- self._start_unix_sock_server(sock_file)
- old_stdout = sys.stdout
- sys.stdout = open(os.devnull, 'w')
- try:
- self.unix._remove_unused_sock_file(sock_file)
- except SystemExit:
- pass
- else:
- # This should never happen
- self.assertTrue(False)
-
- sys.stdout = old_stdout
-
- def test_remove_unused_sock_file_dir(self):
- import tempfile
- dir_name = tempfile.mkdtemp()
- old_stdout = sys.stdout
- sys.stdout = open(os.devnull, 'w')
- try:
- self.unix._remove_unused_sock_file(dir_name)
- except SystemExit:
- pass
- else:
- # This should never happen
- self.assertTrue(False)
-
- sys.stdout = old_stdout
- os.rmdir(dir_name)
-
-if __name__== "__main__":
- unittest.main()
diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in
new file mode 100644
index 0000000..472ef3c
--- /dev/null
+++ b/src/bin/xfrout/tests/xfrout_test.py.in
@@ -0,0 +1,469 @@
+# Copyright (C) 2010 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.
+
+'''Tests for the XfroutSession and UnixSockServer classes '''
+
+
+import unittest
+import os
+from isc.cc.session import *
+from pydnspp import *
+from xfrout import *
+import xfrout
+
+# our fake socket, where we can read and insert messages
+class MySocket():
+ def __init__(self, family, type):
+ self.family = family
+ self.type = type
+ self.sendqueue = bytearray()
+
+ def connect(self, to):
+ pass
+
+ def close(self):
+ pass
+
+ def send(self, data):
+ self.sendqueue.extend(data);
+ return len(data)
+
+ def readsent(self):
+ if len(self.sendqueue) >= 2:
+ size = 2 + struct.unpack("!H", self.sendqueue[:2])[0]
+ else:
+ size = 0
+ result = self.sendqueue[:size]
+ self.sendqueue = self.sendqueue[size:]
+ return result
+
+ def read_msg(self):
+ sent_data = self.readsent()
+ get_msg = Message(Message.PARSE)
+ get_msg.from_wire(bytes(sent_data[2:]))
+ return get_msg
+
+ def clear_send(self):
+ del self.sendqueue[:]
+
+# We subclass the Session class we're testing here, only
+# to override the handle() and _send_data() method
+class MyXfroutSession(XfroutSession):
+ def handle(self):
+ pass
+
+ def _send_data(self, sock, data):
+ size = len(data)
+ total_count = 0
+ while total_count < size:
+ count = sock.send(data[total_count:])
+ total_count += count
+
+class Dbserver:
+ def __init__(self):
+ self._shutdown_event = threading.Event()
+ def get_db_file(self):
+ return None
+ def decrease_transfers_counter(self):
+ pass
+
+class TestXfroutSession(unittest.TestCase):
+ def getmsg(self):
+ msg = Message(Message.PARSE)
+ msg.from_wire(self.mdata)
+ return msg
+
+ def setUp(self):
+ self.sock = MySocket(socket.AF_INET,socket.SOCK_STREAM)
+ self.log = isc.log.NSLogger('xfrout', '', severity = 'critical', log_to_console = False )
+ self.xfrsess = MyXfroutSession(self.sock, None, Dbserver(), self.log)
+ self.mdata = bytes(b'\xd6=\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\xfc\x00\x01')
+ self.soa_record = (4, 3, 'example.com.', 'com.example.', 3600, 'SOA', None, 'master.example.com. admin.example.com. 1234 3600 1800 2419200 7200')
+
+ def test_parse_query_message(self):
+ [get_rcode, get_msg] = self.xfrsess._parse_query_message(self.mdata)
+ self.assertEqual(get_rcode.to_text(), "NOERROR")
+
+ def test_get_query_zone_name(self):
+ msg = self.getmsg()
+ self.assertEqual(self.xfrsess._get_query_zone_name(msg), "example.com.")
+
+ def test_send_data(self):
+ self.xfrsess._send_data(self.sock, self.mdata)
+ senddata = self.sock.readsent()
+ self.assertEqual(senddata, self.mdata)
+
+ def test_reply_xfrout_query_with_error_rcode(self):
+ msg = self.getmsg()
+ self.xfrsess._reply_query_with_error_rcode(msg, self.sock, Rcode(3))
+ get_msg = self.sock.read_msg()
+ self.assertEqual(get_msg.get_rcode().to_text(), "NXDOMAIN")
+
+ def test_send_message(self):
+ msg = self.getmsg()
+ msg.make_response()
+ # soa record data with different cases
+ soa_record = (4, 3, 'Example.com.', 'com.Example.', 3600, 'SOA', None, 'master.Example.com. admin.exAmple.com. 1234 3600 1800 2419200 7200')
+ rrset_soa = self.xfrsess._create_rrset_from_db_record(soa_record)
+ msg.add_rrset(Message.SECTION_ANSWER, rrset_soa)
+ self.xfrsess._send_message(self.sock, msg)
+ send_out_data = self.sock.readsent()[2:]
+
+ # CASE_INSENSITIVE compression mode
+ render = MessageRenderer();
+ render.set_length_limit(XFROUT_MAX_MESSAGE_SIZE)
+ msg.to_wire(render)
+ self.assertNotEqual(render.get_data(), send_out_data)
+
+ # CASE_SENSITIVE compression mode
+ render.clear()
+ render.set_compress_mode(MessageRenderer.CASE_SENSITIVE)
+ render.set_length_limit(XFROUT_MAX_MESSAGE_SIZE)
+ msg.to_wire(render)
+ self.assertEqual(render.get_data(), send_out_data)
+
+ def test_clear_message(self):
+ msg = self.getmsg()
+ qid = msg.get_qid()
+ opcode = msg.get_opcode()
+ rcode = msg.get_rcode()
+
+ self.xfrsess._clear_message(msg)
+ self.assertEqual(msg.get_qid(), qid)
+ self.assertEqual(msg.get_opcode(), opcode)
+ self.assertEqual(msg.get_rcode(), rcode)
+ self.assertTrue(msg.get_header_flag(Message.HEADERFLAG_AA))
+
+ def test_reply_query_with_format_error(self):
+ msg = self.getmsg()
+ self.xfrsess._reply_query_with_format_error(msg, self.sock)
+ get_msg = self.sock.read_msg()
+ self.assertEqual(get_msg.get_rcode().to_text(), "FORMERR")
+
+ def test_create_rrset_from_db_record(self):
+ rrset = self.xfrsess._create_rrset_from_db_record(self.soa_record)
+ self.assertEqual(rrset.get_name().to_text(), "example.com.")
+ self.assertEqual(rrset.get_class(), RRClass("IN"))
+ self.assertEqual(rrset.get_type().to_text(), "SOA")
+ rdata = rrset.get_rdata()
+ self.assertEqual(rdata[0].to_text(), self.soa_record[7])
+
+ def test_send_message_with_last_soa(self):
+ rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
+
+ msg = self.getmsg()
+ msg.make_response()
+ self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa, 0)
+ get_msg = self.sock.read_msg()
+
+ self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 1)
+ self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
+ self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
+
+ #answer_rrset_iter = section_iter(get_msg, section.ANSWER())
+ answer = get_msg.get_section(Message.SECTION_ANSWER)[0]#answer_rrset_iter.get_rrset()
+ self.assertEqual(answer.get_name().to_text(), "example.com.")
+ self.assertEqual(answer.get_class(), RRClass("IN"))
+ self.assertEqual(answer.get_type().to_text(), "SOA")
+ rdata = answer.get_rdata()
+ self.assertEqual(rdata[0].to_text(), self.soa_record[7])
+
+ def test_trigger_send_message_with_last_soa(self):
+ rrset_a = RRset(Name("example.com"), RRClass.IN(), RRType.A(), RRTTL(3600))
+ rrset_a.add_rdata(Rdata(RRType.A(), RRClass.IN(), "192.0.2.1"))
+ rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
+
+ msg = self.getmsg()
+ msg.make_response()
+
+ msg.add_rrset(Message.SECTION_ANSWER, rrset_a)
+ # give the function a value that is larger than MAX-len(rrset)
+ self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa, 65520)
+
+ # this should have triggered the sending of two messages
+ # (1 with the rrset we added manually, and 1 that triggered
+ # the sending in _with_last_soa)
+ get_msg = self.sock.read_msg()
+ self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 1)
+ self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
+ self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
+
+ answer = get_msg.get_section(Message.SECTION_ANSWER)[0]
+ self.assertEqual(answer.get_name().to_text(), "example.com.")
+ self.assertEqual(answer.get_class(), RRClass("IN"))
+ self.assertEqual(answer.get_type().to_text(), "A")
+ rdata = answer.get_rdata()
+ self.assertEqual(rdata[0].to_text(), "192.0.2.1")
+
+ get_msg = self.sock.read_msg()
+ self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 0)
+ self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
+ self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
+
+ #answer_rrset_iter = section_iter(get_msg, Message.SECTION_ANSWER)
+ answer = get_msg.get_section(Message.SECTION_ANSWER)[0]
+ self.assertEqual(answer.get_name().to_text(), "example.com.")
+ self.assertEqual(answer.get_class(), RRClass("IN"))
+ self.assertEqual(answer.get_type().to_text(), "SOA")
+ rdata = answer.get_rdata()
+ self.assertEqual(rdata[0].to_text(), self.soa_record[7])
+
+ # and it should not have sent anything else
+ self.assertEqual(0, len(self.sock.sendqueue))
+
+ def test_get_rrset_len(self):
+ rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
+ self.assertEqual(82, get_rrset_len(rrset_soa))
+
+ def test_zone_has_soa(self):
+ global sqlite3_ds
+ def mydb1(zone, file):
+ return True
+ sqlite3_ds.get_zone_soa = mydb1
+ self.assertTrue(self.xfrsess._zone_has_soa(""))
+ def mydb2(zone, file):
+ return False
+ sqlite3_ds.get_zone_soa = mydb2
+ self.assertFalse(self.xfrsess._zone_has_soa(""))
+
+ def test_zone_exist(self):
+ global sqlite3_ds
+ def zone_exist(zone, file):
+ return zone
+ sqlite3_ds.zone_exist = zone_exist
+ self.assertTrue(self.xfrsess._zone_exist(True))
+ self.assertFalse(self.xfrsess._zone_exist(False))
+
+ def test_check_xfrout_available(self):
+ def zone_exist(zone):
+ return zone
+ def zone_has_soa(zone):
+ return (not zone)
+ self.xfrsess._zone_exist = zone_exist
+ self.xfrsess._zone_has_soa = zone_has_soa
+ self.assertEqual(self.xfrsess._check_xfrout_available(False).to_text(), "NOTAUTH")
+ self.assertEqual(self.xfrsess._check_xfrout_available(True).to_text(), "SERVFAIL")
+
+ def zone_empty(zone):
+ return zone
+ self.xfrsess._zone_has_soa = zone_empty
+ def false_func():
+ return False
+ self.xfrsess._server.increase_transfers_counter = false_func
+ self.assertEqual(self.xfrsess._check_xfrout_available(True).to_text(), "REFUSED")
+ def true_func():
+ return True
+ self.xfrsess._server.increase_transfers_counter = true_func
+ self.assertEqual(self.xfrsess._check_xfrout_available(True).to_text(), "NOERROR")
+
+ def test_dns_xfrout_start_formerror(self):
+ # formerror
+ self.xfrsess.dns_xfrout_start(self.sock, b"\xd6=\x00\x00\x00\x01\x00")
+ sent_data = self.sock.readsent()
+ self.assertEqual(len(sent_data), 0)
+
+ def default(self, param):
+ return "example.com"
+
+ def test_dns_xfrout_start_notauth(self):
+ self.xfrsess._get_query_zone_name = self.default
+ def notauth(formpara):
+ return Rcode.NOTAUTH()
+ self.xfrsess._check_xfrout_available = notauth
+ self.xfrsess.dns_xfrout_start(self.sock, self.mdata)
+ get_msg = self.sock.read_msg()
+ self.assertEqual(get_msg.get_rcode().to_text(), "NOTAUTH")
+
+ def test_dns_xfrout_start_noerror(self):
+ self.xfrsess._get_query_zone_name = self.default
+ def noerror(form):
+ return Rcode.NOERROR()
+ self.xfrsess._check_xfrout_available = noerror
+
+ def myreply(msg, sock, zonename):
+ self.sock.send(b"success")
+
+ self.xfrsess._reply_xfrout_query = myreply
+ self.xfrsess.dns_xfrout_start(self.sock, self.mdata)
+ self.assertEqual(self.sock.readsent(), b"success")
+
+ def test_reply_xfrout_query_noerror(self):
+ global sqlite3_ds
+ def get_zone_soa(zonename, file):
+ return self.soa_record
+
+ def get_zone_datas(zone, file):
+ return [self.soa_record]
+
+ sqlite3_ds.get_zone_soa = get_zone_soa
+ sqlite3_ds.get_zone_datas = get_zone_datas
+ self.xfrsess._reply_xfrout_query(self.getmsg(), self.sock, "example.com.")
+ reply_msg = self.sock.read_msg()
+ self.assertEqual(reply_msg.get_rr_count(Message.SECTION_ANSWER), 2)
+
+class MyCCSession():
+ def __init__(self):
+ pass
+
+ def get_remote_config_value(self, module_name, identifier):
+ if module_name == "Auth" and identifier == "database_file":
+ return "initdb.file", False
+ else:
+ return "unknown", False
+
+
+class MyUnixSockServer(UnixSockServer):
+ def __init__(self):
+ self._lock = threading.Lock()
+ self._transfers_counter = 0
+ self._shutdown_event = threading.Event()
+ self._max_transfers_out = 10
+ self._cc = MyCCSession()
+ self._log = isc.log.NSLogger('xfrout', '', severity = 'critical', log_to_console = False )
+
+class TestUnixSockServer(unittest.TestCase):
+ def setUp(self):
+ self.write_sock, self.read_sock = socket.socketpair()
+ self.unix = MyUnixSockServer()
+
+ def test_receive_query_message(self):
+ send_msg = b"\xd6=\x00\x00\x00\x01\x00"
+ msg_len = struct.pack('H', socket.htons(len(send_msg)))
+ self.write_sock.send(msg_len)
+ self.write_sock.send(send_msg)
+ recv_msg = self.unix._receive_query_message(self.read_sock)
+ self.assertEqual(recv_msg, send_msg)
+
+ def test_updata_config_data(self):
+ self.unix.update_config_data({'transfers_out':10 })
+ self.assertEqual(self.unix._max_transfers_out, 10)
+
+ def test_get_db_file(self):
+ self.assertEqual(self.unix.get_db_file(), "initdb.file")
+
+ def test_increase_transfers_counter(self):
+ self.unix._max_transfers_out = 10
+ count = self.unix._transfers_counter
+ self.assertEqual(self.unix.increase_transfers_counter(), True)
+ self.assertEqual(count + 1, self.unix._transfers_counter)
+
+ self.unix._max_transfers_out = 0
+ count = self.unix._transfers_counter
+ self.assertEqual(self.unix.increase_transfers_counter(), False)
+ self.assertEqual(count, self.unix._transfers_counter)
+
+ def test_decrease_transfers_counter(self):
+ count = self.unix._transfers_counter
+ self.unix.decrease_transfers_counter()
+ self.assertEqual(count - 1, self.unix._transfers_counter)
+
+ def _remove_file(self, sock_file):
+ try:
+ os.remove(sock_file)
+ except OSError:
+ pass
+
+ def test_sock_file_in_use_file_exist(self):
+ sock_file = 'temp.sock.file'
+ self._remove_file(sock_file)
+ self.assertFalse(self.unix._sock_file_in_use(sock_file))
+ self.assertFalse(os.path.exists(sock_file))
+
+ def test_sock_file_in_use_file_not_exist(self):
+ self.assertFalse(self.unix._sock_file_in_use('temp.sock.file'))
+
+ def _start_unix_sock_server(self, sock_file):
+ serv = ThreadingUnixStreamServer(sock_file, BaseRequestHandler)
+ serv_thread = threading.Thread(target=serv.serve_forever)
+ serv_thread.setDaemon(True)
+ serv_thread.start()
+
+ def test_sock_file_in_use(self):
+ sock_file = 'temp.sock.file'
+ self._remove_file(sock_file)
+ self.assertFalse(self.unix._sock_file_in_use(sock_file))
+ self._start_unix_sock_server(sock_file)
+
+ old_stdout = sys.stdout
+ sys.stdout = open(os.devnull, 'w')
+ self.assertTrue(self.unix._sock_file_in_use(sock_file))
+ sys.stdout = old_stdout
+
+ def test_remove_unused_sock_file_in_use(self):
+ sock_file = 'temp.sock.file'
+ self._remove_file(sock_file)
+ self.assertFalse(self.unix._sock_file_in_use(sock_file))
+ self._start_unix_sock_server(sock_file)
+ old_stdout = sys.stdout
+ sys.stdout = open(os.devnull, 'w')
+ try:
+ self.unix._remove_unused_sock_file(sock_file)
+ except SystemExit:
+ pass
+ else:
+ # This should never happen
+ self.assertTrue(False)
+
+ sys.stdout = old_stdout
+
+ def test_remove_unused_sock_file_dir(self):
+ import tempfile
+ dir_name = tempfile.mkdtemp()
+ old_stdout = sys.stdout
+ sys.stdout = open(os.devnull, 'w')
+ try:
+ self.unix._remove_unused_sock_file(dir_name)
+ except SystemExit:
+ pass
+ else:
+ # This should never happen
+ self.assertTrue(False)
+
+ sys.stdout = old_stdout
+ os.rmdir(dir_name)
+
+class TestInitialization(unittest.TestCase):
+ def setEnv(self, name, value):
+ if value is None:
+ if name in os.environ:
+ del os.environ[name]
+ else:
+ os.environ[name] = value
+
+ def setUp(self):
+ self._oldSocket = os.getenv("BIND10_XFROUT_SOCKET_FILE")
+ self._oldFromBuild = os.getenv("B10_FROM_BUILD")
+
+ def tearDown(self):
+ self.setEnv("B10_FROM_BUILD", self._oldFromBuild)
+ self.setEnv("BIND10_XFROUT_SOCKET_FILE", self._oldSocket)
+ # Make sure even the computed values are back
+ xfrout.init_paths()
+
+ def testNoEnv(self):
+ self.setEnv("B10_FROM_BUILD", None)
+ self.setEnv("BIND10_XFROUT_SOCKET_FILE", None)
+ xfrout.init_paths()
+ self.assertEqual(xfrout.UNIX_SOCKET_FILE,
+ "@@LOCALSTATEDIR@@/auth_xfrout_conn")
+
+ def testProvidedSocket(self):
+ self.setEnv("B10_FROM_BUILD", None)
+ self.setEnv("BIND10_XFROUT_SOCKET_FILE", "The/Socket/File")
+ xfrout.init_paths()
+ self.assertEqual(xfrout.UNIX_SOCKET_FILE, "The/Socket/File")
+
+if __name__== "__main__":
+ unittest.main()
diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in
index b3f9e95..17ca3eb 100755
--- a/src/bin/xfrout/xfrout.py.in
+++ b/src/bin/xfrout/xfrout.py.in
@@ -37,29 +37,38 @@ from optparse import OptionParser, OptionValueError
from isc.util import socketserver_mixin
try:
- from libxfr_python import *
+ from libutil_io_python import *
from pydnspp import *
except ImportError as e:
# C++ loadable module may not be installed; even so the xfrout process
# must keep running, so we warn about it and move forward.
- sys.stderr.write('[b10-xfrout] failed to import DNS or XFR module: %s\n' % str(e))
+ sys.stderr.write('[b10-xfrout] failed to import DNS or isc.util.io module: %s\n' % str(e))
isc.util.process.rename()
-if "B10_FROM_BUILD" in os.environ:
- SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/xfrout"
- AUTH_SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/auth"
- if "B10_FROM_SOURCE_LOCALSTATEDIR" in os.environ:
- UNIX_SOCKET_FILE = os.environ["B10_FROM_SOURCE_LOCALSTATEDIR"] + \
- "/auth_xfrout_conn"
+def init_paths():
+ global SPECFILE_PATH
+ global AUTH_SPECFILE_PATH
+ global UNIX_SOCKET_FILE
+ if "B10_FROM_BUILD" in os.environ:
+ SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/xfrout"
+ AUTH_SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/auth"
+ if "B10_FROM_SOURCE_LOCALSTATEDIR" in os.environ:
+ UNIX_SOCKET_FILE = os.environ["B10_FROM_SOURCE_LOCALSTATEDIR"] + \
+ "/auth_xfrout_conn"
+ else:
+ UNIX_SOCKET_FILE = os.environ["B10_FROM_BUILD"] + "/auth_xfrout_conn"
else:
- UNIX_SOCKET_FILE = os.environ["B10_FROM_BUILD"] + "/auth_xfrout_conn"
-else:
- PREFIX = "@prefix@"
- DATAROOTDIR = "@datarootdir@"
- SPECFILE_PATH = "@datadir@/@PACKAGE@".replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX)
- AUTH_SPECFILE_PATH = SPECFILE_PATH
- UNIX_SOCKET_FILE = "@@LOCALSTATEDIR@@/auth_xfrout_conn"
+ PREFIX = "@prefix@"
+ DATAROOTDIR = "@datarootdir@"
+ SPECFILE_PATH = "@datadir@/@PACKAGE@".replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX)
+ AUTH_SPECFILE_PATH = SPECFILE_PATH
+ if "BIND10_XFROUT_SOCKET_FILE" in os.environ:
+ UNIX_SOCKET_FILE = os.environ["BIND10_XFROUT_SOCKET_FILE"]
+ else:
+ UNIX_SOCKET_FILE = "@@LOCALSTATEDIR@@/auth_xfrout_conn"
+
+init_paths()
SPECFILE_LOCATION = SPECFILE_PATH + "/xfrout.spec"
AUTH_SPECFILE_LOCATION = AUTH_SPECFILE_PATH + os.sep + "auth.spec"
@@ -376,7 +385,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
# This may happen when one xfrout process try to connect to
# xfrout unix socket server, to check whether there is another
# xfrout running.
- if sock_fd == XFR_FD_RECEIVE_FAIL:
+ if sock_fd == FD_COMM_ERROR:
self._log.log_message("error", "Failed to receive the file descriptor for XFR connection")
return
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 8525b8d..a5f67e5 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -1,2 +1,2 @@
-SUBDIRS = exceptions dns cc config python xfr bench log asiolink \
+SUBDIRS = exceptions dns cc config util python xfr bench log asiolink \
nsas cache resolve testutils datasrc server_common
diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am
new file mode 100644
index 0000000..3e74708
--- /dev/null
+++ b/src/lib/util/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = io unittests io/tests
+# The io/tests is hack, because otherwise we can not order these directories
+# properly. Unittests use io and io/tests use unittest.
diff --git a/src/lib/util/io/Makefile.am b/src/lib/util/io/Makefile.am
new file mode 100644
index 0000000..b2653d8
--- /dev/null
+++ b/src/lib/util/io/Makefile.am
@@ -0,0 +1,16 @@
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+lib_LTLIBRARIES = libutil_io.la
+libutil_io_la_SOURCES = fd.h fd.cc fd_share.h fd_share.cc
+libutil_io_la_CXXFLAGS = $(AM_CXXFLAGS) -fno-strict-aliasing
+
+CLEANFILES = *.gcno *.gcda
+
+pyexec_LTLIBRARIES = libutil_io_python.la
+# Python prefers .so, while some OSes (specifically MacOS) use a different
+# suffix for dynamic objects. -module is necessary to work this around.
+libutil_io_python_la_LDFLAGS = -module
+libutil_io_python_la_SOURCES = fdshare_python.cc
+libutil_io_python_la_LIBADD = libutil_io.la
+libutil_io_python_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
+libutil_io_python_la_CXXFLAGS = $(AM_CXXFLAGS)
diff --git a/src/lib/util/io/fd.cc b/src/lib/util/io/fd.cc
new file mode 100644
index 0000000..04e64dc
--- /dev/null
+++ b/src/lib/util/io/fd.cc
@@ -0,0 +1,70 @@
+// 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 "fd.h"
+
+#include <unistd.h>
+#include <cerrno>
+
+namespace isc {
+namespace util {
+namespace io {
+
+bool
+write_data(const int fd, const void *buffer_v, const size_t length) {
+ const unsigned char *buffer(static_cast<const unsigned char *>(buffer_v));
+ size_t rest(length);
+ // Just keep writing until all is written
+ while (rest) {
+ ssize_t written(write(fd, buffer, rest));
+ if (rest == -1) {
+ if (errno == EINTR) { // Just keep going
+ continue;
+ } else {
+ return false;
+ }
+ } else { // Wrote something
+ rest -= written;
+ buffer += written;
+ }
+ }
+ return true;
+}
+
+ssize_t
+read_data(const int fd, void *buffer_v, const size_t length) {
+ unsigned char *buffer(static_cast<unsigned char *>(buffer_v));
+ size_t rest(length), already(0);
+ while (rest) { // Stil something to read
+ ssize_t amount(read(fd, buffer, rest));
+ if (rest == -1) {
+ if (errno == EINTR) { // Continue on interrupted call
+ continue;
+ } else {
+ return -1;
+ }
+ } else if (amount) {
+ already += amount;
+ rest -= amount;
+ buffer += amount;
+ } else { // EOF
+ return already;
+ }
+ }
+ return already;
+}
+
+}
+}
+}
diff --git a/src/lib/util/io/fd.h b/src/lib/util/io/fd.h
new file mode 100644
index 0000000..bdd2d41
--- /dev/null
+++ b/src/lib/util/io/fd.h
@@ -0,0 +1,61 @@
+// 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 __UTIL_IO_FD_H
+#define __UTIL_IO_FD_H 1
+
+#include <unistd.h>
+
+/**
+ * @file fd.h
+ * @short Wrappers around common unix fd manipulation functions.
+ */
+
+namespace isc {
+namespace util {
+namespace io {
+
+/*
+ * \short write() that writes everything.
+ * Wrapper around write(). The difference is, it never writes less data
+ * and looks successfull (eg. it blocks until all data are written).
+ * Retries on signals.
+ *
+ * \return True if sucessfull, false otherwise. The errno variable is left
+ * intact.
+ * \param fd Where to write.
+ * \param data The buffer to write.
+ * \param length How much data is there to write.
+ */
+bool
+write_data(const int fd, const void *data, const size_t length);
+
+/*
+ * \short read() that reads everything.
+ * Wrapper around read(). It does not do short reads, if it returns less,
+ * it means there was EOF. It retries on signals.
+ *
+ * \return Number of bytes read or -1 on error.
+ * \param fd Where to read data from.
+ * \param data Where to put the data.
+ * \param length How many of them.
+ */
+ssize_t
+read_data(const int fd, void *buffer, const size_t length);
+
+}
+}
+}
+
+#endif // __UTIL_IO_FD_H
diff --git a/src/lib/util/io/fd_share.cc b/src/lib/util/io/fd_share.cc
new file mode 100644
index 0000000..92576e0
--- /dev/null
+++ b/src/lib/util/io/fd_share.cc
@@ -0,0 +1,139 @@
+// Copyright (C) 2010 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 <cstring>
+#include <cstdlib>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <stdlib.h> // for malloc and free
+#include "fd_share.h"
+
+namespace isc {
+namespace util {
+namespace io {
+
+namespace {
+// Not all OSes support advanced CMSG macros: CMSG_LEN and CMSG_SPACE.
+// In order to ensure as much portability as possible, we provide wrapper
+// functions of these macros.
+// Note that cmsg_space() could run slow on OSes that do not have
+// CMSG_SPACE.
+inline socklen_t
+cmsg_len(const socklen_t len) {
+#ifdef CMSG_LEN
+ return (CMSG_LEN(len));
+#else
+ // Cast NULL so that any pointer arithmetic performed by CMSG_DATA
+ // is correct.
+ const uintptr_t hdrlen = (uintptr_t)CMSG_DATA(((struct cmsghdr*)NULL));
+ return (hdrlen + len);
+#endif
+}
+
+inline socklen_t
+cmsg_space(const socklen_t len) {
+#ifdef CMSG_SPACE
+ return (CMSG_SPACE(len));
+#else
+ struct msghdr msg;
+ struct cmsghdr* cmsgp;
+ // XXX: The buffer length is an ad hoc value, but should be enough
+ // in a practical sense.
+ char dummybuf[sizeof(struct cmsghdr) + 1024];
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_control = dummybuf;
+ msg.msg_controllen = sizeof(dummybuf);
+
+ cmsgp = (struct cmsghdr*)dummybuf;
+ cmsgp->cmsg_len = cmsg_len(len);
+
+ cmsgp = CMSG_NXTHDR(&msg, cmsgp);
+ if (cmsgp != NULL) {
+ return ((char*)cmsgp - (char*)msg.msg_control);
+ } else {
+ return (0);
+ }
+#endif // CMSG_SPACE
+}
+}
+
+int
+recv_fd(const int sock) {
+ struct msghdr msghdr;
+ struct iovec iov_dummy;
+ unsigned char dummy_data;
+
+ iov_dummy.iov_base = &dummy_data;
+ iov_dummy.iov_len = sizeof(dummy_data);
+ msghdr.msg_name = NULL;
+ msghdr.msg_namelen = 0;
+ msghdr.msg_iov = &iov_dummy;
+ msghdr.msg_iovlen = 1;
+ msghdr.msg_flags = 0;
+ msghdr.msg_controllen = cmsg_space(sizeof(int));
+ msghdr.msg_control = malloc(msghdr.msg_controllen);
+ if (msghdr.msg_control == NULL) {
+ return (FD_OTHER_ERROR);
+ }
+
+ if (recvmsg(sock, &msghdr, 0) < 0) {
+ free(msghdr.msg_control);
+ return (FD_COMM_ERROR);
+ }
+ const struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msghdr);
+ int fd = FD_OTHER_ERROR;
+ if (cmsg != NULL && cmsg->cmsg_len == cmsg_len(sizeof(int)) &&
+ cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+ fd = *(const int*)CMSG_DATA(cmsg);
+ }
+ free(msghdr.msg_control);
+ return (fd);
+}
+
+int
+send_fd(const int sock, const int fd) {
+ struct msghdr msghdr;
+ struct iovec iov_dummy;
+ unsigned char dummy_data = 0;
+
+ iov_dummy.iov_base = &dummy_data;
+ iov_dummy.iov_len = sizeof(dummy_data);
+ msghdr.msg_name = NULL;
+ msghdr.msg_namelen = 0;
+ msghdr.msg_iov = &iov_dummy;
+ msghdr.msg_iovlen = 1;
+ msghdr.msg_flags = 0;
+ msghdr.msg_controllen = cmsg_space(sizeof(int));
+ msghdr.msg_control = malloc(msghdr.msg_controllen);
+ if (msghdr.msg_control == NULL) {
+ return (FD_OTHER_ERROR);
+ }
+
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msghdr);
+ cmsg->cmsg_len = cmsg_len(sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ *(int*)CMSG_DATA(cmsg) = fd;
+
+ const int ret = sendmsg(sock, &msghdr, 0);
+ free(msghdr.msg_control);
+ return (ret >= 0 ? 0 : FD_COMM_ERROR);
+}
+
+} // End for namespace io
+} // End for namespace util
+} // End for namespace isc
diff --git a/src/lib/util/io/fd_share.h b/src/lib/util/io/fd_share.h
new file mode 100644
index 0000000..b74d4ef
--- /dev/null
+++ b/src/lib/util/io/fd_share.h
@@ -0,0 +1,65 @@
+// 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 FD_SHARE_H_
+#define FD_SHARE_H_
+
+/**
+ * \file fd_share.h
+ * \short Support to transfer file descriptors between processes.
+ * \todo This interface is very C-ish. Should we have some kind of exceptions?
+ */
+
+namespace isc {
+namespace util {
+namespace io {
+
+const int FD_COMM_ERROR = -2;
+const int FD_OTHER_ERROR = -1;
+
+/**
+ * \short Receives a file descriptor.
+ * This receives a file descriptor sent over an unix domain socket. This
+ * is the counterpart of send_fd().
+ *
+ * \return FD_COMM_ERROR when there's error receiving the socket, FD_OTHER_ERROR
+ * when there's a different error.
+ * \param sock The unix domain socket to read from. Tested and it does
+ * not work with a pipe.
+ */
+int recv_fd(const int sock);
+
+/**
+ * \short Sends a file descriptor.
+ * This sends a file descriptor over an unix domain socket. This is the
+ * counterpart of recv_fd().
+ *
+ * \return FD_COMM_ERROR when there's error sending the socket, FD_OTHER_ERROR
+ * for all other possible errors.
+ * \param sock The unix domain socket to send to. Tested and it does not
+ * work with a pipe.
+ * \param fd The file descriptor to send. It should work with any valid
+ * file descriptor.
+ */
+int send_fd(const int sock, const int fd);
+
+} // End for namespace io
+} // End for namespace util
+} // End for namespace isc
+
+#endif
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/io/fdshare_python.cc b/src/lib/util/io/fdshare_python.cc
new file mode 100644
index 0000000..0a41107
--- /dev/null
+++ b/src/lib/util/io/fdshare_python.cc
@@ -0,0 +1,97 @@
+// Copyright (C) 2010 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.
+
+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
+#include <structmember.h>
+
+#include <config.h>
+
+#include "fd_share.h"
+
+
+static PyObject*
+fdshare_recv_fd(PyObject*, PyObject* args) {
+ int sock, fd;
+ if (!PyArg_ParseTuple(args, "i", &sock)) {
+ return (NULL);
+ }
+ fd = isc::util::io::recv_fd(sock);
+ return (Py_BuildValue("i", fd));
+}
+
+static PyObject*
+fdshare_send_fd(PyObject*, PyObject* args) {
+ int sock, fd, result;
+ if (!PyArg_ParseTuple(args, "ii", &sock, &fd)) {
+ return (NULL);
+ }
+ result = isc::util::io::send_fd(sock, fd);
+ return (Py_BuildValue("i", result));
+}
+
+static PyMethodDef fdshare_Methods[] = {
+ {"send_fd", fdshare_send_fd, METH_VARARGS, ""},
+ {"recv_fd", fdshare_recv_fd, METH_VARARGS, ""},
+ {NULL, NULL, 0, NULL} /* Sentinel */
+};
+
+
+static PyModuleDef bind10_fdshare_python = {
+ { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
+ "bind10_fdshare",
+ "Python bindings for fdshare",
+ -1,
+ fdshare_Methods,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+PyMODINIT_FUNC
+PyInit_libutil_io_python(void) {
+ PyObject *mod = PyModule_Create(&bind10_fdshare_python);
+ if (mod == NULL) {
+ return (NULL);
+ }
+
+ PyObject* FD_COMM_ERROR = Py_BuildValue("i", isc::util::io::FD_COMM_ERROR);
+ if (FD_COMM_ERROR == NULL) {
+ Py_XDECREF(mod);
+ return (NULL);
+ }
+ int ret = PyModule_AddObject(mod, "FD_COMM_ERROR", FD_COMM_ERROR);
+ if (-1 == ret) {
+ Py_XDECREF(FD_COMM_ERROR);
+ Py_XDECREF(mod);
+ return (NULL);
+ }
+
+ PyObject* FD_OTHER_ERROR = Py_BuildValue("i",
+ isc::util::io::FD_OTHER_ERROR);
+ if (FD_OTHER_ERROR == NULL) {
+ Py_XDECREF(mod);
+ return (NULL);
+ }
+ ret = PyModule_AddObject(mod, "FD_OTHER_ERROR", FD_OTHER_ERROR);
+ if (-1 == ret) {
+ Py_XDECREF(FD_OTHER_ERROR);
+ Py_XDECREF(mod);
+ return (NULL);
+ }
+
+ return (mod);
+}
+
diff --git a/src/lib/util/io/tests/Makefile.am b/src/lib/util/io/tests/Makefile.am
new file mode 100644
index 0000000..56d50cf
--- /dev/null
+++ b/src/lib/util/io/tests/Makefile.am
@@ -0,0 +1,25 @@
+CLEANFILES = *.gcno *.gcda
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += fd_tests.cc
+run_unittests_SOURCES += fd_share_tests.cc
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD += $(top_builddir)/src/lib/util/io/libutil_io.la
+run_unittests_LDADD += \
+ $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/util/io/tests/fd_share_tests.cc b/src/lib/util/io/tests/fd_share_tests.cc
new file mode 100644
index 0000000..0902ce0
--- /dev/null
+++ b/src/lib/util/io/tests/fd_share_tests.cc
@@ -0,0 +1,74 @@
+// 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 "../fd.h"
+#include "../fd_share.h"
+
+#include <util/unittests/fork.h>
+
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <cstdio>
+
+using namespace isc::util::io;
+using namespace isc::util::unittests;
+
+namespace {
+
+// We test that we can transfer a pipe over other pipe
+TEST(FDShare, transfer) {
+ // Get a pipe and fork
+ int pipes[2];
+ ASSERT_NE(-1, socketpair(AF_UNIX, SOCK_STREAM, 0, pipes));
+ pid_t sender(fork());
+ ASSERT_NE(-1, sender);
+ if(sender) { // We are in parent
+ // Close the other side of pipe, we want only writible one
+ EXPECT_NE(-1, close(pipes[0]));
+ // Get a process to check data
+ int fd(0);
+ pid_t checker(check_output(&fd, "data", 4));
+ ASSERT_NE(-1, checker);
+ // Now, send the file descriptor, close it and close the pipe
+ EXPECT_NE(-1, send_fd(pipes[1], fd));
+ EXPECT_NE(-1, close(pipes[1]));
+ EXPECT_NE(-1, close(fd));
+ // Check both subprocesses ended well
+ EXPECT_TRUE(process_ok(sender));
+ EXPECT_TRUE(process_ok(checker));
+ } else { // We are in child. We do not use ASSERT here
+ // Close the write end, we only read
+ if(close(pipes[1])) {
+ exit(1);
+ }
+ // Get the file descriptor
+ int fd(recv_fd(pipes[0]));
+ if(fd == -1) {
+ exit(1);
+ }
+ // This pipe is not needed
+ if(close(pipes[0])) {
+ exit(1);
+ }
+ // Send "data" trough the received fd, close it and be done
+ if(!write_data(fd, "data", 4) || close(fd) == -1) {
+ exit(1);
+ }
+ exit(0);
+ }
+}
+
+}
diff --git a/src/lib/util/io/tests/fd_tests.cc b/src/lib/util/io/tests/fd_tests.cc
new file mode 100644
index 0000000..12b70d8
--- /dev/null
+++ b/src/lib/util/io/tests/fd_tests.cc
@@ -0,0 +1,66 @@
+// 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 "../fd.h"
+
+#include <util/unittests/fork.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc::util::io;
+using namespace isc::util::unittests;
+
+namespace {
+
+// Make sure the test is large enough and does not fit into one
+// read or write request
+const size_t TEST_DATA_SIZE = 8 * 1024 * 1024;
+
+class FDTest : public ::testing::Test {
+ public:
+ unsigned char *data, *buffer;
+ FDTest() :
+ // We do not care what is inside, we just need it to be the same
+ data(new unsigned char[TEST_DATA_SIZE]),
+ buffer(NULL)
+ { }
+ ~ FDTest() {
+ delete[] data;
+ delete[] buffer;
+ }
+};
+
+// Test we read what was sent
+TEST_F(FDTest, read) {
+ int read_pipe(0);
+ buffer = new unsigned char[TEST_DATA_SIZE];
+ pid_t feeder(provide_input(&read_pipe, data, TEST_DATA_SIZE));
+ ASSERT_GE(feeder, 0);
+ ssize_t received(read_data(read_pipe, buffer, TEST_DATA_SIZE));
+ EXPECT_TRUE(process_ok(feeder));
+ EXPECT_EQ(TEST_DATA_SIZE, received);
+ EXPECT_EQ(0, memcmp(data, buffer, received));
+}
+
+// Test we write the correct thing
+TEST_F(FDTest, write) {
+ int write_pipe(0);
+ pid_t checker(check_output(&write_pipe, data, TEST_DATA_SIZE));
+ ASSERT_GE(checker, 0);
+ EXPECT_TRUE(write_data(write_pipe, data, TEST_DATA_SIZE));
+ EXPECT_EQ(0, close(write_pipe));
+ EXPECT_TRUE(process_ok(checker));
+}
+
+}
diff --git a/src/lib/util/io/tests/run_unittests.cc b/src/lib/util/io/tests/run_unittests.cc
new file mode 100644
index 0000000..e787ab1
--- /dev/null
+++ b/src/lib/util/io/tests/run_unittests.cc
@@ -0,0 +1,22 @@
+// 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 <gtest/gtest.h>
+
+int
+main(int argc, char *argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ return RUN_ALL_TESTS();
+}
diff --git a/src/lib/util/unittests/Makefile.am b/src/lib/util/unittests/Makefile.am
new file mode 100644
index 0000000..0ea8653
--- /dev/null
+++ b/src/lib/util/unittests/Makefile.am
@@ -0,0 +1,9 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+lib_LTLIBRARIES = libutil_unittests.la
+libutil_unittests_la_SOURCES = fork.h fork.cc
+libutil_unittests_la_LIBADD = \
+ $(top_builddir)/src/lib/util/io/libutil_io.la
+
+CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/util/unittests/README b/src/lib/util/unittests/README
new file mode 100644
index 0000000..0ed888f
--- /dev/null
+++ b/src/lib/util/unittests/README
@@ -0,0 +1,5 @@
+This directory contains some code that is useful while writing various
+unittest code. It doesn't contain any code that would actually run in
+bind10.
+
+Because this is a test code, we do not test it explicitly.
diff --git a/src/lib/util/unittests/fork.cc b/src/lib/util/unittests/fork.cc
new file mode 100644
index 0000000..1eeb2cc
--- /dev/null
+++ b/src/lib/util/unittests/fork.cc
@@ -0,0 +1,145 @@
+// 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 "fork.h"
+
+#include <util/io/fd.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <signal.h>
+#include <cstring>
+#include <cerrno>
+#include <cstdlib>
+#include <cstdio>
+
+using namespace isc::util::io;
+
+namespace {
+
+// Just a NOP function to ignore a signal but let it interrupt function.
+void no_handler(int) { }
+
+};
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+bool
+process_ok(pid_t process) {
+ // Create a timeout
+ struct sigaction ignored, original;
+ memset(&ignored, 0, sizeof ignored);
+ ignored.sa_handler = no_handler;
+ if (sigaction(SIGALRM, &ignored, &original)) {
+ return false;
+ }
+ // It is long, but if everything is OK, it'll not happen
+ alarm(10);
+ int status;
+ int result(waitpid(process, &status, 0) == -1);
+ // Cancel the alarm and return the original handler
+ alarm(0);
+ if (sigaction(SIGALRM, &original, NULL)) {
+ return false;
+ }
+ // Check what we found out
+ if (result) {
+ if (errno == EINTR)
+ kill(process, SIGTERM);
+ return false;
+ }
+ return WIFEXITED(status) && WEXITSTATUS(status) == 0;
+}
+
+/*
+ * This creates a pipe, forks and feeds the pipe with given data.
+ * Used to provide the input in non-blocking/asynchronous way.
+ */
+pid_t
+provide_input(int *read_pipe, const void *input, const size_t length)
+{
+ int pipes[2];
+ if (pipe(pipes)) {
+ return -1;
+ }
+ *read_pipe = pipes[0];
+ pid_t pid(fork());
+ if (pid) { // We are in the parent
+ return pid;
+ } else { // This is in the child, just puth the data there
+ close(pipes[0]);
+ if (!write_data(pipes[1], input, length)) {
+ exit(1);
+ } else {
+ close(pipes[1]);
+ exit(0);
+ }
+ }
+}
+
+/*
+ * This creates a pipe, forks and reads the pipe and compares it
+ * with given data. Used to check output of run in asynchronous way.
+ */
+pid_t
+check_output(int *write_pipe, const void *output, const size_t length)
+{
+ int pipes[2];
+ if (pipe(pipes)) {
+ return -1;
+ }
+ *write_pipe = pipes[1];
+ pid_t pid(fork());
+ if (pid) { // We are in parent
+ close(pipes[0]);
+ return pid;
+ } else {
+ close(pipes[1]);
+ // We don't return the memory, but we're in tests and end this process
+ // right away.
+ unsigned char *buffer = new unsigned char[length + 1];
+ // Try to read one byte more to see if the output ends here
+ size_t got_length(read_data(pipes[0], buffer, length + 1));
+ bool ok(true);
+ if (got_length != length) {
+ fprintf(stderr, "Different length (expected %u, got %u)\n",
+ static_cast<unsigned>(length),
+ static_cast<unsigned>(got_length));
+ ok = false;
+ }
+ if(!ok || memcmp(buffer, output, length)) {
+ const unsigned char *output_c(static_cast<const unsigned char *>(
+ output));
+ // If they differ, print what we have
+ for(size_t i(0); i != got_length; ++ i) {
+ fprintf(stderr, "%02hhx", buffer[i]);
+ }
+ fprintf(stderr, "\n");
+ for(size_t i(0); i != length; ++ i) {
+ fprintf(stderr, "%02hhx", output_c[i]);
+ }
+ fprintf(stderr, "\n");
+ exit(1);
+ } else {
+ exit(0);
+ }
+ }
+}
+
+}
+}
+}
diff --git a/src/lib/util/unittests/fork.h b/src/lib/util/unittests/fork.h
new file mode 100644
index 0000000..3331cfa
--- /dev/null
+++ b/src/lib/util/unittests/fork.h
@@ -0,0 +1,52 @@
+// 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 __UTIL_UNITTESTS_FORK_H
+#define __UTIL_UNITTESTS_FORK_H 1
+
+#include <unistd.h>
+
+/**
+ * @file fork.h
+ * @short Help functions to fork the test case process.
+ * Various functions to fork a process and feed some data to pipe, check
+ * its output and such lives here.
+ */
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+/**
+ * @short Checks that a process terminates correctly.
+ * Waits for a process to terminate (with a short timeout, this should be
+ * used whan the process is about tu terminate) and checks its exit code.
+ *
+ * @return True if the process terminates with 0, false otherwise.
+ * @param process The ID of process to wait for.
+ */
+bool
+process_ok(pid_t process);
+
+pid_t
+provide_input(int *read_pipe, const void *input, const size_t length);
+
+pid_t
+check_output(int *write_pipe, const void *output, const size_t length);
+
+} // End of the namespace
+}
+}
+
+#endif // __UTIL_UNITTESTS_FORK_H
diff --git a/src/lib/xfr/Makefile.am b/src/lib/xfr/Makefile.am
index 79cee73..d714990 100644
--- a/src/lib/xfr/Makefile.am
+++ b/src/lib/xfr/Makefile.am
@@ -2,7 +2,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
AM_CPPFLAGS += $(BOOST_INCLUDES)
-AM_CXXFLAGS = $(B10_CXXFLAGS) -Wno-strict-aliasing
+AM_CXXFLAGS = $(B10_CXXFLAGS)
AM_CXXFLAGS += -Wno-unused-parameter # see src/lib/cc/Makefile.am
if USE_CLANGPP
AM_CXXFLAGS += -Wno-error
@@ -11,13 +11,5 @@ endif
CLEANFILES = *.gcno *.gcda
lib_LTLIBRARIES = libxfr.la
-libxfr_la_SOURCES = xfrout_client.h xfrout_client.cc
-libxfr_la_SOURCES += fd_share.h fd_share.cc
-
-pyexec_LTLIBRARIES = libxfr_python.la
-# Python prefers .so, while some OSes (specifically MacOS) use a different
-# suffix for dynamic objects. -module is necessary to work this around.
-libxfr_python_la_LDFLAGS = -module
-libxfr_python_la_SOURCES = fdshare_python.cc fd_share.cc fd_share.h
-libxfr_python_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
-libxfr_python_la_CXXFLAGS = $(AM_CXXFLAGS)
+libxfr_la_SOURCES = xfrout_client.h xfrout_client.cc
+libxfr_la_LIBADD = $(top_builddir)/src/lib/util/io/libutil_io.la
diff --git a/src/lib/xfr/fd_share.cc b/src/lib/xfr/fd_share.cc
deleted file mode 100644
index 4e1f093..0000000
--- a/src/lib/xfr/fd_share.cc
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright (C) 2010 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 <cstring>
-#include <cstdlib>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-#include <stdlib.h> // for malloc and free
-#include <xfr/fd_share.h>
-
-namespace isc {
-namespace xfr {
-
-namespace {
-// Not all OSes support advanced CMSG macros: CMSG_LEN and CMSG_SPACE.
-// In order to ensure as much portability as possible, we provide wrapper
-// functions of these macros.
-// Note that cmsg_space() could run slow on OSes that do not have
-// CMSG_SPACE.
-inline socklen_t
-cmsg_len(const socklen_t len) {
-#ifdef CMSG_LEN
- return (CMSG_LEN(len));
-#else
- // Cast NULL so that any pointer arithmetic performed by CMSG_DATA
- // is correct.
- const uintptr_t hdrlen = (uintptr_t)CMSG_DATA(((struct cmsghdr*)NULL));
- return (hdrlen + len);
-#endif
-}
-
-inline socklen_t
-cmsg_space(const socklen_t len) {
-#ifdef CMSG_SPACE
- return (CMSG_SPACE(len));
-#else
- struct msghdr msg;
- struct cmsghdr* cmsgp;
- // XXX: The buffer length is an ad hoc value, but should be enough
- // in a practical sense.
- char dummybuf[sizeof(struct cmsghdr) + 1024];
-
- memset(&msg, 0, sizeof(msg));
- msg.msg_control = dummybuf;
- msg.msg_controllen = sizeof(dummybuf);
-
- cmsgp = (struct cmsghdr*)dummybuf;
- cmsgp->cmsg_len = cmsg_len(len);
-
- cmsgp = CMSG_NXTHDR(&msg, cmsgp);
- if (cmsgp != NULL) {
- return ((char*)cmsgp - (char*)msg.msg_control);
- } else {
- return (0);
- }
-#endif // CMSG_SPACE
-}
-}
-
-int
-recv_fd(const int sock) {
- struct msghdr msghdr;
- struct iovec iov_dummy;
- unsigned char dummy_data;
-
- iov_dummy.iov_base = &dummy_data;
- iov_dummy.iov_len = sizeof(dummy_data);
- msghdr.msg_name = NULL;
- msghdr.msg_namelen = 0;
- msghdr.msg_iov = &iov_dummy;
- msghdr.msg_iovlen = 1;
- msghdr.msg_flags = 0;
- msghdr.msg_controllen = cmsg_space(sizeof(int));
- msghdr.msg_control = malloc(msghdr.msg_controllen);
- if (msghdr.msg_control == NULL) {
- return (-1);
- }
-
- if (recvmsg(sock, &msghdr, 0) < 0) {
- free(msghdr.msg_control);
- return (XFR_FD_RECEIVE_FAIL);
- }
- const struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msghdr);
- int fd = -1;
- if (cmsg != NULL && cmsg->cmsg_len == cmsg_len(sizeof(int)) &&
- cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
- fd = *(const int*)CMSG_DATA(cmsg);
- }
- free(msghdr.msg_control);
- return (fd);
-}
-
-int
-send_fd(const int sock, const int fd) {
- struct msghdr msghdr;
- struct iovec iov_dummy;
- unsigned char dummy_data = 0;
-
- iov_dummy.iov_base = &dummy_data;
- iov_dummy.iov_len = sizeof(dummy_data);
- msghdr.msg_name = NULL;
- msghdr.msg_namelen = 0;
- msghdr.msg_iov = &iov_dummy;
- msghdr.msg_iovlen = 1;
- msghdr.msg_flags = 0;
- msghdr.msg_controllen = cmsg_space(sizeof(int));
- msghdr.msg_control = malloc(msghdr.msg_controllen);
- if (msghdr.msg_control == NULL) {
- return (-1);
- }
-
- struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msghdr);
- cmsg->cmsg_len = cmsg_len(sizeof(int));
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- *(int*)CMSG_DATA(cmsg) = fd;
-
- const int ret = sendmsg(sock, &msghdr, 0);
- free(msghdr.msg_control);
- return (ret >= 0 ? 0 : -1);
-}
-
-} // End for namespace xfr
-} // End for namespace isc
diff --git a/src/lib/xfr/fd_share.h b/src/lib/xfr/fd_share.h
deleted file mode 100644
index 4ee5fd5..0000000
--- a/src/lib/xfr/fd_share.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) 2010 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 FD_SHARE_H_
-#define FD_SHARE_H_
-
-namespace isc {
-namespace xfr {
-
-/// Failed to receive xfr socket descriptor "fd" on unix domain socket 'sock'
-const int XFR_FD_RECEIVE_FAIL = -2;
-
-// Receive socket descriptor on unix domain socket 'sock'.
-// Returned value is the socket descriptor received.
-// Returned XFR_FD_RECEIVE_FAIL if failed to receive xfr socket descriptor
-// Errors are indicated by a return value of -1.
-int recv_fd(const int sock);
-
-// Send socket descriptor "fd" to server over unix domain socket 'sock',
-// the connection from socket 'sock' to unix domain server should be established first.
-// Errors are indicated by a return value of -1.
-int send_fd(const int sock, const int fd);
-
-} // End for namespace xfr
-} // End for namespace isc
-
-#endif
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/xfr/fdshare_python.cc b/src/lib/xfr/fdshare_python.cc
deleted file mode 100644
index 82b1b6e..0000000
--- a/src/lib/xfr/fdshare_python.cc
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (C) 2010 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.
-
-#define PY_SSIZE_T_CLEAN
-#include <Python.h>
-#include <structmember.h>
-
-#include <config.h>
-
-#include <xfr/fd_share.h>
-
-
-static PyObject*
-fdshare_recv_fd(PyObject*, PyObject* args) {
- int sock, fd;
- if (!PyArg_ParseTuple(args, "i", &sock)) {
- return (NULL);
- }
- fd = isc::xfr::recv_fd(sock);
- return (Py_BuildValue("i", fd));
-}
-
-static PyObject*
-fdshare_send_fd(PyObject*, PyObject* args) {
- int sock, fd, result;
- if (!PyArg_ParseTuple(args, "ii", &sock, &fd)) {
- return (NULL);
- }
- result = isc::xfr::send_fd(sock, fd);
- return (Py_BuildValue("i", result));
-}
-
-static PyMethodDef fdshare_Methods[] = {
- {"send_fd", fdshare_send_fd, METH_VARARGS, ""},
- {"recv_fd", fdshare_recv_fd, METH_VARARGS, ""},
- {NULL, NULL, 0, NULL} /* Sentinel */
-};
-
-
-static PyModuleDef bind10_fdshare_python = {
- { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
- "bind10_fdshare",
- "Python bindings for fdshare",
- -1,
- fdshare_Methods,
- NULL,
- NULL,
- NULL,
- NULL
-};
-
-PyMODINIT_FUNC
-PyInit_libxfr_python(void) {
- PyObject* mod = PyModule_Create(&bind10_fdshare_python);
- if (mod == NULL) {
- return (NULL);
- }
-
- PyObject* XFR_FD_RECEIVE_FAIL = Py_BuildValue("i", isc::xfr::XFR_FD_RECEIVE_FAIL);
- if (XFR_FD_RECEIVE_FAIL == NULL) {
- Py_XDECREF(mod);
- return (NULL);
- }
- int ret = PyModule_AddObject(mod, "XFR_FD_RECEIVE_FAIL", XFR_FD_RECEIVE_FAIL);
- if (-1 == ret) {
- Py_XDECREF(XFR_FD_RECEIVE_FAIL);
- Py_XDECREF(mod);
- return (NULL);
- }
-
- return (mod);
-}
-
diff --git a/src/lib/xfr/python_xfr.cc b/src/lib/xfr/python_xfr.cc
deleted file mode 100644
index 52848ad..0000000
--- a/src/lib/xfr/python_xfr.cc
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2010 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 <boost/python.hpp>
-#include <boost/python/class.hpp>
-#include <boost/python/module.hpp>
-#include <boost/python/def.hpp>
-#include <boost/python/exception_translator.hpp>
-#include <boost/python/return_internal_reference.hpp>
-#include <boost/python/copy_const_reference.hpp>
-#include <boost/shared_ptr.hpp>
-
-#include <xfr/fd_share.h>
-
-using namespace isc::xfr;
-using namespace boost::python;
-
-BOOST_PYTHON_MODULE(bind10_xfr) {
- def("recv_fd", &recv_fd);
- def("send_fd", &send_fd);
-}
diff --git a/src/lib/xfr/xfrout_client.cc b/src/lib/xfr/xfrout_client.cc
index e9e736b..6ab905b 100644
--- a/src/lib/xfr/xfrout_client.cc
+++ b/src/lib/xfr/xfrout_client.cc
@@ -20,10 +20,11 @@
#include <unistd.h>
#include <asio.hpp>
-#include <xfr/fd_share.h>
+#include <util/io/fd_share.h>
#include <xfr/xfrout_client.h>
using namespace std;
+using namespace isc::util::io;
using asio::local::stream_protocol;
namespace isc {
@@ -72,7 +73,7 @@ XfroutClient::sendXfroutRequestInfo(const int tcp_sock,
const void* const msg_data,
const uint16_t msg_len)
{
- if (-1 == send_fd(impl_->socket_.native(), tcp_sock)) {
+ if (send_fd(impl_->socket_.native(), tcp_sock) < 0) {
isc_throw(XfroutError,
"Failed to send the socket file descriptor "
"to xfrout module");
More information about the bind10-changes
mailing list