+++ /dev/null
-/kea-sockcreator
-/kea-sockcreator.8
+++ /dev/null
-SUBDIRS = . tests
-
-AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
-
-AM_CXXFLAGS = $(KEA_CXXFLAGS)
-
-if USE_STATIC_LINK
-AM_LDFLAGS = -static
-endif
-
-pkglibexecdir = $(libexecdir)/@PACKAGE@
-
-CLEANFILES = *.gcno *.gcda
-
-man_MANS = kea-sockcreator.8
-DISTCLEANFILES = $(man_MANS)
-EXTRA_DIST = $(man_MANS) kea-sockcreator.xml
-
-if GENERATE_DOCS
-
-kea-sockcreator.8: kea-sockcreator.xml
- @XSLTPROC@ --novalid --xinclude @NONET@ -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/kea-sockcreator.xml
-
-else
-
-$(man_MANS):
- @echo Man generation disabled. Creating dummy $@. Configure with --enable-generate-docs to enable it.
- @echo Man generation disabled. Remove this file, configure with --enable-generate-docs, and rebuild Kea > $@
-
-endif
-
-# convenience archive
-
-noinst_LTLIBRARIES = libsockcreator.la
-
-libsockcreator_la_SOURCES = sockcreator.cc sockcreator.h
-
-pkglibexec_PROGRAMS = kea-sockcreator
-
-kea_sockcreator_SOURCES = main.cc
-kea_sockcreator_LDADD = libsockcreator.la
-kea_sockcreator_LDADD += $(top_builddir)/src/lib/util/io/libkea-util-io.la
-kea_sockcreator_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+++ /dev/null
-August 2014: As of Kea 0.9, this code is not built. It is currently uncertain
-whether it will be needed in a future version Kea, so it is being retained
-in the repository for now: it may be deleted in the future.
-
-
-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 rest drops 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 minimize 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 endianness), 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).
+++ /dev/null
-<!--
- - Copyright (C) 2012-2019 Internet Systems Consortium, Inc. ("ISC")
- -
- - This Source Code Form is subject to the terms of the Mozilla Public
- - License, v. 2.0. If a copy of the MPL was not distributed with this
- - file, You can obtain one at http://mozilla.org/MPL/2.0/.
--->
-
-<refentry>
-
- <refentryinfo>
- <productname>ISC Kea</productname>
- <date>July 24, 2019</date>
- <edition>1.6.0-beta2</edition>
- <author>
- <contrib>The Kea software has been written by a number of
- engineers working for ISC: Tomek Mrugalski, Stephen Morris, Marcin
- Siodelski, Thomas Markwalder, Francis Dupont, Jeremy C. Reed,
- Wlodek Wencel and Shawn Routhier. That list is roughly in the
- chronological order in which the authors made their first
- contribution. For a complete list of authors and
- contributors, see AUTHORS file.</contrib>
- <orgname>Internet Systems Consortium, Inc.</orgname>
- </author>
- </refentryinfo>
-
- <info>
- <date>2018-06-14</date>
- </info>
-
- <refmeta>
- <refentrytitle>kea-sockcreator</refentrytitle>
- <manvolnum>8</manvolnum>
- <refmiscinfo>Kea</refmiscinfo>
- </refmeta>
-
- <refnamediv>
- <refname>kea-sockcreator</refname>
- <refpurpose>socket creation daemon</refpurpose>
- </refnamediv>
-
- <docinfo>
- <copyright>
- <year>2014-2019</year>
- <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
- </copyright>
- </docinfo>
-
- <refsynopsisdiv>
- <cmdsynopsis sepchar=" ">
- <command>kea-sockcreator</command>
- </cmdsynopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>DESCRIPTION</title>
- <para>
- The <command>kea-sockcreator</command> daemon's entire job
- is to create sockets and assign names to them.
- </para>
-
-<!-- TODO: integrate README here -->
-
- <para>
- The <command>kea-sockcreator</command> daemon will exit
- if there is an unrecoverable error or unknown command.
- </para>
-
- </refsect1>
-
- <refsect1>
- <title>SEE ALSO</title>
- <para>
- <citetitle>Kea Guide</citetitle>.
- </para>
-<!-- TODO: point to developer docs -->
- </refsect1>
-
- <refsect1>
- <title>HISTORY</title>
- <para>
- The <command>b10-sockcreator</command> daemon
- was initially designed by Michal Vaner of CZNIC.
- </para>
- <para>
- Kea became a standalone server and the BIND10 framework was
- removed. The socket creator server binary was renamed to
- kea-sockcreator in July 2014.
- </para>
- </refsect1>
-
-</refentry>
+++ /dev/null
-// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#include <config.h>
-
-#include "sockcreator.h"
-#include <unistd.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.
- */
- try {
- run(STDIN_FILENO, STDOUT_FILENO, getSock, isc::util::io::send_fd, close);
- } catch (const SocketCreatorError& ec) {
- return (ec.getExitCode());
- }
- return (0);
-}
+++ /dev/null
-// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#include <config.h>
-
-#include "sockcreator.h"
-
-#include <util/io/fd.h>
-#include <util/io/sockaddr_util.h>
-
-#include <cerrno>
-#include <string.h>
-
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-
-using namespace isc::util::io;
-using namespace isc::util::io::internal;
-using namespace isc::socket_creator;
-
-namespace {
-
-// Simple wrappers for read_data/write_data that throw an exception on error.
-void
-readMessage(const int fd, void* where, const size_t length) {
- if (read_data(fd, where, length) < length) {
- isc_throw(ReadError, "Error reading from socket creator client");
- }
-}
-
-void
-writeMessage(const int fd, const void* what, const size_t length) {
- if (!write_data(fd, what, length)) {
- isc_throw(WriteError, "Error writing to socket creator client");
- }
-}
-
-// Exit on a protocol error after informing the client of the problem.
-void
-protocolError(const int fd, const char reason = 'I') {
-
- // Tell client we have a problem
- char message[2];
- message[0] = 'F';
- message[1] = reason;
- writeMessage(fd, message, sizeof(message));
-
- // ... and exit
- isc_throw(ProtocolError, "Fatal error, reason: " << reason);
-}
-
-// Return appropriate socket type constant for the socket type requested.
-// The output_fd argument is required to report a protocol error.
-int
-getSocketType(const char type_code, const int output_fd) {
- int socket_type = 0;
- switch (type_code) {
- case 'T':
- socket_type = SOCK_STREAM;
- break;
-
- case 'U':
- socket_type = SOCK_DGRAM;
- break;
-
- default:
- protocolError(output_fd); // Does not return
- }
- return (socket_type);
-}
-
-// Convert return status from getSock() to a character to be sent back to
-// the caller.
-char
-getErrorCode(const int status) {
- char error_code = ' ';
- switch (status) {
- case -1:
- error_code = 'S';
- break;
-
- case -2:
- error_code = 'B';
- break;
-
- default:
- isc_throw(InternalError, "Error creating socket");
- }
- return (error_code);
-}
-
-
-// Handle the request from the client.
-//
-// Reads the type and family of socket required, creates the socket and returns
-// it to the client.
-//
-// The arguments passed (and the exceptions thrown) are the same as those for
-// run().
-void
-handleRequest(const int input_fd, const int output_fd,
- const get_sock_t get_sock, const send_fd_t send_fd_fun,
- const close_t close_fun)
-{
- // Read the message from the client
- char type[2];
- readMessage(input_fd, type, sizeof(type));
-
- // Decide what type of socket is being asked for
- const int sock_type = getSocketType(type[0], output_fd);
-
- // Read the address they ask for depending on what address family was
- // specified.
- sockaddr* addr = NULL;
- size_t addr_len = 0;
- sockaddr_in addr_in;
- sockaddr_in6 addr_in6;
- switch (type[1]) { // The address family
-
- // The casting to apparently incompatible types is required by the
- // C low-level interface.
-
- case '4':
- addr = convertSockAddr(&addr_in);
- addr_len = sizeof(addr_in);
- memset(&addr_in, 0, sizeof(addr_in));
- addr_in.sin_family = AF_INET;
- readMessage(input_fd, &addr_in.sin_port, sizeof(addr_in.sin_port));
- readMessage(input_fd, &addr_in.sin_addr.s_addr,
- sizeof(addr_in.sin_addr.s_addr));
- break;
-
- case '6':
- addr = convertSockAddr(&addr_in6);
- addr_len = sizeof(addr_in6);
- memset(&addr_in6, 0, sizeof(addr_in6));
- addr_in6.sin6_family = AF_INET6;
- readMessage(input_fd, &addr_in6.sin6_port,
- sizeof(addr_in6.sin6_port));
- readMessage(input_fd, &addr_in6.sin6_addr.s6_addr,
- sizeof(addr_in6.sin6_addr.s6_addr));
- break;
-
- default:
- protocolError(output_fd);
- }
-
- // Obtain the socket
- const int result = get_sock(sock_type, addr, addr_len, close_fun);
- if (result >= 0) {
- // Got the socket, send it to the client.
- writeMessage(output_fd, "S", 1);
- if (send_fd_fun(output_fd, result) != 0) {
- // Error. Close the socket (ignore any error from that operation)
- // and abort.
- close_fun(result);
- isc_throw(InternalError, "Error sending descriptor");
- }
-
- // Successfully sent the socket, so free up resources we still hold
- // for it.
- if (close_fun(result) == -1) {
- isc_throw(InternalError, "Error closing socket");
- }
- } else {
- // Error. Tell the client.
- char error_message[2];
- error_message[0] = 'E';
- error_message[1] = getErrorCode(result);
- writeMessage(output_fd, error_message, sizeof(error_message));
-
- // ...and append the reason code to the error message
- const int error_number = errno;
- writeMessage(output_fd, &error_number, sizeof(error_number));
- }
-}
-
-// Sets the MTU related flags for IPv6 UDP sockets.
-// It is borrowed from bind-9 lib/isc/unix/socket.c and modified
-// to compile here.
-//
-// The function returns -2 if it fails or the socket file descriptor
-// on success (for convenience, so the result can be just returned).
-int
-mtu(int fd) {
-#ifdef IPV6_USE_MIN_MTU /* RFC 3542, not too common yet*/
- const int on(1);
- // use minimum MTU
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_USE_MIN_MTU, &on, sizeof(on)) < 0) {
- return (-2);
- }
-#else // Try the following as fallback
-#ifdef IPV6_MTU
- // Use minimum MTU on systems that don't have the IPV6_USE_MIN_MTU
- const int mtu = 1280;
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_MTU, &mtu, sizeof(mtu)) < 0) {
- return (-2);
- }
-#endif
-#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DONT)
- // Turn off Path MTU discovery on IPv6/UDP sockets.
- const int action = IPV6_PMTUDISC_DONT;
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &action,
- sizeof(action)) < 0) {
-
- return (-2);
- }
-#endif
-#endif
- return (fd);
-}
-
-// This one closes the socket if result is negative. Used not to leak socket
-// on error.
-int maybeClose(const int result, const int socket, const close_t close_fun) {
- if (result < 0) {
- if (close_fun(socket) == -1) {
- isc_throw(InternalError, "Error closing socket");
- }
- }
- return (result);
-}
-
-} // Anonymous namespace
-
-namespace isc {
-namespace socket_creator {
-
-// Get the socket and bind to it.
-int
-getSock(const int type, struct sockaddr* bind_addr, const socklen_t addr_len,
- const close_t close_fun) {
- const int sock = socket(bind_addr->sa_family, type, 0);
- if (sock == -1) {
- return (-1);
- }
- const int on = 1;
- if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
- // This is part of the binding process, so it's a bind error
- return (maybeClose(-2, sock, close_fun));
- }
- if (bind_addr->sa_family == AF_INET6 &&
- setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1) {
- // This is part of the binding process, so it's a bind error
- return (maybeClose(-2, sock, close_fun));
- }
- if (bind(sock, bind_addr, addr_len) == -1) {
- return (maybeClose(-2, sock, close_fun));
- }
- if (type == SOCK_DGRAM && bind_addr->sa_family == AF_INET6) {
- // Set some MTU flags on IPv6 UDP sockets.
- return (maybeClose(mtu(sock), sock, close_fun));
- }
- return (sock);
-}
-
-// Main run loop.
-void
-run(const int input_fd, const int output_fd, get_sock_t get_sock,
- send_fd_t send_fd_fun, close_t close_fun)
-{
- for (;;) {
- char command;
- readMessage(input_fd, &command, sizeof(command));
- switch (command) {
- case 'S': // The "get socket" command
- handleRequest(input_fd, output_fd, get_sock,
- send_fd_fun, close_fun);
- break;
-
- case 'T': // The "terminate" command
- return;
-
- default: // Don't recognize anything else
- protocolError(output_fd);
- }
- }
-}
-
-} // End of the namespaces
-}
+++ /dev/null
-// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-/// \file sockcreator.h
-/// \short Socket creator functionality.
-///
-/// This module holds the functionality of the socket creator. It is a separate
-/// module from main to make testing easier.
-
-#ifndef SOCKCREATOR_H
-#define SOCKCREATOR_H 1
-
-#include <util/io/fd_share.h>
-#include <exceptions/exceptions.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <unistd.h>
-
-namespace isc {
-namespace socket_creator {
-
-// Exception classes - the base class exception SocketCreatorError is caught
-// by main() and holds an exit code returned to the environment. The code
-// depends on the exact exception raised.
-class SocketCreatorError : public isc::Exception {
-public:
- SocketCreatorError(const char* file, size_t line, const char* what,
- int exit_code) :
- isc::Exception(file, line, what), exit_code_(exit_code) {}
-
- int getExitCode() const {
- return (exit_code_);
- }
-
-private:
- const int exit_code_; // Code returned to exit()
-};
-
-class ReadError : public SocketCreatorError {
-public:
- ReadError(const char* file, size_t line, const char* what) :
- SocketCreatorError(file, line, what, 1) {}
-};
-
-class WriteError : public SocketCreatorError {
-public:
- WriteError(const char* file, size_t line, const char* what) :
- SocketCreatorError(file, line, what, 2) {}
-};
-
-class ProtocolError : public SocketCreatorError {
-public:
- ProtocolError(const char* file, size_t line, const char* what) :
- SocketCreatorError(file, line, what, 3) {}
-};
-
-class InternalError : public SocketCreatorError {
-public:
- InternalError(const char* file, size_t line, const char* what) :
- SocketCreatorError(file, line, what, 4) {}
-};
-
-
-// Type of the close() function, so it can be passed as a parameter.
-// Argument is the same as that for close(2).
-typedef int (*close_t)(int);
-
-/// \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.
-///
-/// \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.
-/// \param close_fun The function used to close a socket if there's an error
-/// after the creation.
-///
-/// \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 left intact from socket() or bind()).
-int
-getSock(const int type, struct sockaddr* bind_addr, const socklen_t addr_len,
- const close_t close_fun);
-
-// Define some types for functions used to perform socket-related operations.
-// These are typedef'ed so that alternatives can be passed through to the
-// main functions for testing purposes.
-
-// Type of the function to get a socket and to pass it as parameter.
-// Arguments are those described above for getSock().
-typedef int (*get_sock_t)(const int, struct sockaddr *, const socklen_t,
- const close_t close_fun);
-
-// Type of the send_fd() function, so it can be passed as a parameter.
-// Arguments are the same as those of the send_fd() function.
-typedef int (*send_fd_t)(const int, const int);
-
-
-/// \brief 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.
-///
-/// It terminates either if a command asks it to or when unrecoverable error
-/// happens.
-///
-/// \param input_fd File number of the stream from which the input commands
-/// are read.
-/// \param output_fd File number of the stream to which the response is
-/// written. The socket is sent as part of a control message associated
-/// with that stream.
-/// \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.
-/// \param close_fun The close function used to close sockets, coming from
-/// unistd.h. It can be overridden in tests.
-///
-/// \exception isc::socket_creator::ReadError Error reading from input
-/// \exception isc::socket_creator::WriteError Error writing to output
-/// \exception isc::socket_creator::ProtocolError Unrecognized command received
-/// \exception isc::socket_creator::InternalError Other error
-void
-run(const int input_fd, const int output_fd, get_sock_t get_sock_fun,
- send_fd_t send_fd_fun, close_t close_fun);
-
-} // namespace socket_creator
-} // NAMESPACE ISC
-
-#endif // SOCKCREATOR_H
+++ /dev/null
-/run_unittests
+++ /dev/null
-CLEANFILES = *.gcno *.gcda
-
-AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
-AM_CPPFLAGS += $(BOOST_INCLUDES)
-AM_CXXFLAGS = $(KEA_CXXFLAGS)
-
-if USE_STATIC_LINK
-AM_LDFLAGS = -static
-endif
-
-TESTS_ENVIRONMENT = \
- $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
-
-TESTS =
-if HAVE_GTEST
-TESTS += run_unittests
-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/bin/sockcreator/libsockcreator.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/io/libkea-util-io.la
-endif
-
-noinst_PROGRAMS = $(TESTS)
+++ /dev/null
-// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#include <config.h>
-
-#include <gtest/gtest.h>
-#include <util/unittests/run_all.h>
-
-int
-main(int argc, char *argv[]) {
- ::testing::InitGoogleTest(&argc, argv);
-
- return isc::util::unittests::run_all();
-}
+++ /dev/null
-// Copyright (C) 2011-2017 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#include <config.h>
-
-#include "../sockcreator.h"
-
-#include <util/unittests/fork.h>
-#include <util/io/fd.h>
-
-#include <boost/lexical_cast.hpp>
-#include <gtest/gtest.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <unistd.h>
-
-#include <iostream>
-#include <cstring>
-#include <cerrno>
-
-using namespace isc::socket_creator;
-using namespace isc::util::unittests;
-using namespace isc::util::io;
-
-// The tests check both TCP and UDP sockets on IPv4 and IPv6.
-//
-// Essentially we need to check all four combinations of TCP/UDP and IPv4/IPv6.
-// The different address families (IPv4/IPv6) require different structures to
-// hold the address information, and so some common code is in the form of
-// templates (or overloads), parameterized on the structure type.
-//
-// The protocol is determined by an integer (SOCK_STREAM or SOCK_DGRAM) so
-// cannot be templated in the same way. Relevant check functions are
-// selected manually.
-
-namespace {
-
-// Set IP-version-specific fields.
-
-void
-setAddressFamilyFields(sockaddr_in* address) {
- address->sin_family = AF_INET;
- address->sin_addr.s_addr = INADDR_ANY;
-}
-
-void
-setAddressFamilyFields(sockaddr_in6* address) {
- address->sin6_family = AF_INET6;
- address->sin6_addr = in6addr_loopback;
-}
-
-// Socket has been opened, perform a check on it. The sole argument is the
-// socket descriptor. The TCP check is the same regardless of the address
-// family. The UDP check requires that the socket address be obtained so
-// is parameterized on the type of structure required to hold the address.
-
-void
-tcpCheck(const int socknum) {
- // Sufficient to be able to listen on the socket.
- EXPECT_EQ(0, listen(socknum, 1));
-}
-
-template <typename ADDRTYPE>
-void
-udpCheck(const int socknum) {
- // UDP testing is more complicated than TCP: send a packet to ourselves and
- // see if it arrives.
-
- // Get details of the socket so that we can use it as the target of the
- // sendto().
- ADDRTYPE addr;
- memset(&addr, 0, sizeof(addr));
- sockaddr* addr_ptr = reinterpret_cast<sockaddr*>(&addr);
- socklen_t len = sizeof(addr);
- ASSERT_EQ(0, getsockname(socknum, addr_ptr, &len));
-
- // Send the packet to ourselves and check we receive it.
- ASSERT_EQ(5, sendto(socknum, "test", 5, 0, addr_ptr, sizeof(addr))) <<
- "Send failed with error " << strerror(errno) << " on socket " <<
- socknum;
- char buffer[5];
- ASSERT_EQ(5, recv(socknum, buffer, 5, 0)) <<
- "Recv failed with error " << strerror(errno) << " on socket " <<
- socknum;
- EXPECT_STREQ("test", buffer);
-}
-
-// The check function (tcpCheck/udpCheck) is passed as a parameter to the test
-// code, so provide a convenient typedef.
-typedef void (*socket_check_t)(const int);
-
-// Address-family-specific socket checks.
-//
-// The first argument is used to select the overloaded check function.
-// The other argument is the socket descriptor number.
-
-// IPv4 check
-void addressFamilySpecificCheck(const sockaddr_in*, const int, const int) {
-}
-
-// IPv6 check
-void addressFamilySpecificCheck(const sockaddr_in6*, const int socknum,
- const int socket_type)
-{
- int options;
- socklen_t len = sizeof(options);
- EXPECT_EQ(0, getsockopt(socknum, IPPROTO_IPV6, IPV6_V6ONLY, &options,
- &len));
- EXPECT_NE(0, options);
- if (socket_type == SOCK_DGRAM) {
- // Some more checks for UDP - MTU
-#ifdef IPV6_USE_MIN_MTU /* RFC 3542, not too common yet*/
- // use minimum MTU
- EXPECT_EQ(0, getsockopt(socknum, IPPROTO_IPV6, IPV6_USE_MIN_MTU,
- &options, &len)) << strerror(errno);
- EXPECT_NE(0, options);
-#else
- // We do not check for the IPV6_MTU, because while setting works (eg.
- // the packets are fragmented correctly), the getting does not. If
- // we try to getsockopt it, an error complaining it can't be accessed
- // on unconnected socket is returned. If we try to connect it, the
- // MTU of the interface is returned, not the one we set. So we live
- // in belief it works because we examined the packet dump.
-#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DONT)
- // Turned off Path MTU discovery on IPv6/UDP sockets?
- EXPECT_EQ(0, getsockopt(socknum, IPPROTO_IPV6, IPV6_MTU_DISCOVER,
- &options, &len)) << strerror(errno);
- EXPECT_EQ(IPV6_PMTUDISC_DONT, options);
-#endif
-#endif
- }
-}
-
-// Just ignore the fd and pretend success. We close invalid fds in the tests.
-int
-closeIgnore(int) {
- return (0);
-}
-
-// Generic version of the socket test. It creates the socket and checks that
-// it is a valid descriptor. The family-specific check functions are called
-// to check that the socket is valid. The function is parameterized according
-// to the structure used to hold the address.
-//
-// Arguments:
-// socket_type Type of socket to create (SOCK_DGRAM or SOCK_STREAM)
-// socket_check Associated check function - udpCheck() or tcpCheck()
-template <typename ADDRTYPE>
-void testAnyCreate(int socket_type, socket_check_t socket_check) {
-
- // Create the socket.
- ADDRTYPE addr;
- memset(&addr, 0, sizeof(addr));
- setAddressFamilyFields(&addr);
- sockaddr* addr_ptr = reinterpret_cast<sockaddr*>(&addr);
- const int socket = getSock(socket_type, addr_ptr, sizeof(addr),
- closeIgnore);
- ASSERT_GE(socket, 0) << "Couldn't create socket: failed with " <<
- "return code " << socket << " and error " << strerror(errno);
-
- // Perform socket-type-specific testing.
- socket_check(socket);
-
- // Do address-family-independent
- int options;
- socklen_t len = sizeof(options);
- EXPECT_EQ(0, getsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &options, &len));
- EXPECT_NE(0, options);
-
- // ...and the address-family specific tests.
- addressFamilySpecificCheck(&addr, socket, socket_type);
-
- // Tidy up and exit.
- EXPECT_EQ(0, close(socket));
-}
-
-
-// Several tests to ensure we can create the sockets.
-TEST(get_sock, udp4_create) {
- testAnyCreate<sockaddr_in>(SOCK_DGRAM, udpCheck<sockaddr_in>);
-}
-
-TEST(get_sock, tcp4_create) {
- testAnyCreate<sockaddr_in>(SOCK_STREAM, tcpCheck);
-}
-
-#ifdef HAVE_BROKEN_GET_IPV6_USE_MIN_MTU
-TEST(get_sock, DISABLED_udp6_create)
-#else
-TEST(get_sock, udp6_create)
-#endif
-{
- testAnyCreate<sockaddr_in6>(SOCK_DGRAM, udpCheck<sockaddr_in6>);
-}
-
-TEST(get_sock, tcp6_create) {
- testAnyCreate<sockaddr_in6>(SOCK_STREAM, tcpCheck);
-}
-
-bool close_called(false);
-
-// You can use it as a close mockup. If you care about checking if it was really
-// called, you can use the close_called variable. But set it to false before the
-// test.
-int closeCall(int socket) {
- close(socket);
- close_called = true;
- return (0);
-}
-
-// Ask the get_sock function for some nonsense and test if it is able to report
-// an error.
-TEST(get_sock, fail_with_nonsense) {
- sockaddr addr;
- memset(&addr, 0, sizeof(addr));
- close_called = false;
- ASSERT_EQ(-1, getSock(0, &addr, sizeof addr, closeCall));
- ASSERT_FALSE(close_called); // The "socket" call should have failed already
-}
-
-// Bind should have failed here
-TEST(get_sock, fail_with_bind) {
- sockaddr_in addr;
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = 1;
- // No host should have this address on the interface, so it should not be
- // possible to bind it.
- addr.sin_addr.s_addr = inet_addr("192.0.2.1");
- close_called = false;
- ASSERT_EQ(-2, getSock(SOCK_STREAM, reinterpret_cast<sockaddr*>(&addr),
- sizeof addr, closeCall));
- ASSERT_TRUE(close_called); // The "socket" call should have failed already
-}
-
-// The main run() function in the socket creator takes three functions to
-// get the socket, send information to it, and close it. These allow for
-// alternatives to the system functions to be used for testing.
-
-// Replacement getSock() function.
-// The return value indicates the result of checks and is encoded. Using LSB
-// bit numbering (least-significant bit is bit 0) then:
-//
-// bit 0: 1 if "type" is known, 0 otherwise
-// bit 1: 1 for UDP, 0 for TCP
-// bit 2: 1 if address family is known, 0 otherwise
-// bit 3: 1 for IPv6, 0 for IPv4
-// bit 4: 1 if port passed was valid
-//
-// Other possible return values are:
-//
-// -1: The simulated bind() call has failed
-// -2: The simulated socket() call has failed
-int
-getSockDummy(const int type, struct sockaddr* addr, const socklen_t,
- const close_t) {
- int result = 0;
- int port = 0;
-
- // Validate type field
- switch (type) {
- case SOCK_STREAM:
- result |= 0x01;
- break;
-
- case SOCK_DGRAM:
- result |= 0x03;
- break;
- }
-
- // Validate address family
- switch (addr->sa_family) {
- case AF_INET:
- result |= 0x04;
- port = reinterpret_cast<sockaddr_in*>(addr)->sin_port;
- break;
-
- case AF_INET6:
- result |= 0x0C;
- port = reinterpret_cast<sockaddr_in6*>(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 |= 0x10;
- }
- }
- return (result);
-}
-
-// Dummy send function - return data (the result of getSock()) to the destination.
-int
-send_FdDummy(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. And even with the LS byte, we can distinguish between
- // the different results.
- const char fd_data = what & 0xff;
- const bool status = write_data(destination, &fd_data, sizeof(fd_data));
- return (status ? 0 : -1);
-}
-
-// 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 runTest(const char* input_data, const size_t input_size,
- const char* output_data, const size_t output_size,
- bool should_succeed = true,
- const close_t test_close = closeIgnore,
- const send_fd_t send_fd = send_FdDummy)
-{
- // Prepare the input feeder and output checker processes. The feeder
- // process sends data from the client to run() and the checker process
- // reads the response and checks it against the expected response.
- int input_fd = 0;
- const pid_t input = provide_input(&input_fd, input_data, input_size);
- ASSERT_NE(-1, input) << "Couldn't start input feeder";
-
- int output_fd = 0;
- const pid_t output = check_output(&output_fd, output_data, output_size);
- ASSERT_NE(-1, output) << "Couldn't start output checker";
-
- // Run the body
- if (should_succeed) {
- EXPECT_NO_THROW(run(input_fd, output_fd, getSockDummy, send_fd,
- test_close));
- } else {
- EXPECT_THROW(run(input_fd, output_fd, getSockDummy, send_fd,
- test_close), isc::socket_creator::SocketCreatorError);
- }
-
- // Close the pipes
- close(input_fd);
- close(output_fd);
-
- // 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) {
- runTest("T", 1, NULL, 0);
-}
-
-// Check it rejects incorrect input.
-TEST(run, bad_input) {
- runTest("XXX", 3, "FI", 2, false);
-}
-
-// Check it correctly parses query stream to create sockets.
-TEST(run, sockets) {
- runTest(
- // Commands:
- "SU4\xff\xff\0\0\0\0" // IPv4 UDP socket, port 0xffffff, address 0.0.0.0
- "ST4\xff\xff\0\0\0\0" // IPv4 TCP socket, port 0xffffff, address 0.0.0.0
- "ST6\xff\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- // IPv6 UDP socket, port 0xffffff, address ::
- "SU6\xff\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- // IPv6 TCP socket, port 0xffffff, address ::
- "T", // ... and terminate
- 9 + 9 + 21 + 21 + 1, // Length of command string
- "S\x07S\x05S\x0dS\x0f", // Response ("S" + LS byte of getSock() return)
- 8); // Length of response
-}
-
-
-// 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. We expect
- // two failure answers in this test, each answer comprising two characters
- // followed by the (int) errno value.
- char result[2 * (2 + sizeof(int))];
-
- // We expect the errno parts to be zero but the characters to depend on the
- // exact failure.
- memset(result, 0, sizeof(result));
- strcpy(result, "EB");
- strcpy(result + 2 + sizeof(int), "ES");
-
- // Run the test
- runTest(
- "SU4\xbb\xbb\0\0\0\0" // Port number will trigger simulated bind() fail
- "SU4\xcc\xcc\0\0\0\0" // Port number will trigger simulated socket() fail
- "T", // Terminate
- 19, // Length of command string
- result, sizeof(result));
-}
-
-// A close that fails. (This causes an abort.)
-int
-closeFail(int) {
- return (-1);
-}
-
-TEST(run, cant_close) {
- runTest("SU4\xff\xff\0\0\0\0", 9,
- "S\x07", 2,
- false, closeFail);
-}
-
-// A send of the file descriptor that fails. In this case we expect the client
-// to receive the "S" indicating that the descriptor is being sent and nothing
-// else. This causes an abort.
-int
-sendFDFail(const int, const int) {
- return (FD_SYSTEM_ERROR);
-}
-
-TEST(run, cant_send_fd) {
- runTest("SU4\xff\xff\0\0\0\0", 9,
- "S", 1,
- false, closeIgnore, sendFDFail);
-}
-
-} // Anonymous namespace