sockcreator_tests.cc 12.2 KB
Newer Older
1
// Copyright (C) 2011-2012  Internet Systems Consortium, Inc. ("ISC")
Michal Vaner's avatar
Michal Vaner committed
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//
// 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"

Michal Vaner's avatar
Michal Vaner committed
17
18
19
#include <util/unittests/fork.h>
#include <util/io/fd.h>

20
#include <boost/lexical_cast.hpp>
Michal Vaner's avatar
Michal Vaner committed
21
22
23
24
#include <gtest/gtest.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
Michal Vaner's avatar
Michal Vaner committed
25
#include <unistd.h>
26
27

#include <iostream>
Michal Vaner's avatar
Michal Vaner committed
28
29
30
31
#include <cstring>
#include <cerrno>

using namespace isc::socket_creator;
Michal Vaner's avatar
Michal Vaner committed
32
33
using namespace isc::util::unittests;
using namespace isc::util::io;
Michal Vaner's avatar
Michal Vaner committed
34

35
36
37
38
39
40
41
42
43
44
45
// 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), parameterised 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.

Michal Vaner's avatar
Michal Vaner committed
46
47
namespace {

48
// Set IP-version-specific fields.
Michal Vaner's avatar
Michal Vaner committed
49

50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
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, peform 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 parameterised 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 conveniet typedef.
typedef void (*socket_check_t)(const int);

// Address-family-specific scoket 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) {
109
}
110
111
112
113
114
115
116

// IPv6 check
void addressFamilySpecificCheck(const sockaddr_in6*, const int socknum) {
    int options;
    socklen_t len = sizeof(options);
    EXPECT_EQ(0, getsockopt(socknum, IPPROTO_IPV6, IPV6_V6ONLY, &options, &len));
    EXPECT_NE(0, options);
117
}
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135


// 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 parameterised 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);
136
    const int socket = getSock(socket_type, addr_ptr, sizeof(addr));
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
    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);

    // Tidy up and exit.
    EXPECT_EQ(0, close(socket));
}


// Several tests to ensure we can create the sockets.
Michal Vaner's avatar
Michal Vaner committed
158
TEST(get_sock, udp4_create) {
159
    testAnyCreate<sockaddr_in>(SOCK_DGRAM, udpCheck<sockaddr_in>);
Michal Vaner's avatar
Michal Vaner committed
160
161
162
}

TEST(get_sock, tcp4_create) {
163
    testAnyCreate<sockaddr_in>(SOCK_STREAM, tcpCheck);
Michal Vaner's avatar
Michal Vaner committed
164
165
166
}

TEST(get_sock, udp6_create) {
167
    testAnyCreate<sockaddr_in6>(SOCK_DGRAM, udpCheck<sockaddr_in6>);
Michal Vaner's avatar
Michal Vaner committed
168
169
170
}

TEST(get_sock, tcp6_create) {
171
    testAnyCreate<sockaddr_in6>(SOCK_STREAM, tcpCheck);
Michal Vaner's avatar
Michal Vaner committed
172
173
}

174
175
// Ask the get_sock function for some nonsense and test if it is able to report
// an error.
Michal Vaner's avatar
Michal Vaner committed
176
TEST(get_sock, fail_with_nonsense) {
177
178
    sockaddr addr;
    memset(&addr, 0, sizeof(addr));
179
    ASSERT_LT(getSock(0, &addr, sizeof addr), 0);
Michal Vaner's avatar
Michal Vaner committed
180
181
}

182
183
184
185
// 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.

186
// Replacement getSock() function.
187
188
189
190
191
192
193
194
195
196
197
198
199
// 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
Michal Vaner's avatar
Michal Vaner committed
200
int
201
getSockDummy(const int type, struct sockaddr* addr, const socklen_t) {
202
203
204
205
    int result = 0;
    int port = 0;

    // Validate type field
Michal Vaner's avatar
Michal Vaner committed
206
    switch (type) {
Michal Vaner's avatar
Michal Vaner committed
207
        case SOCK_STREAM:
208
            result |= 0x01;
Michal Vaner's avatar
Michal Vaner committed
209
            break;
210

Michal Vaner's avatar
Michal Vaner committed
211
        case SOCK_DGRAM:
212
            result |= 0x03;
Michal Vaner's avatar
Michal Vaner committed
213
214
            break;
    }
215
216

    // Validate address family
Michal Vaner's avatar
Michal Vaner committed
217
    switch (addr->sa_family) {
Michal Vaner's avatar
Michal Vaner committed
218
        case AF_INET:
219
220
            result |= 0x04;
            port = reinterpret_cast<sockaddr_in*>(addr)->sin_port;
Michal Vaner's avatar
Michal Vaner committed
221
            break;
222

Michal Vaner's avatar
Michal Vaner committed
223
        case AF_INET6:
224
225
            result |= 0x0C;
            port = reinterpret_cast<sockaddr_in6*>(addr)->sin6_port;
Michal Vaner's avatar
Michal Vaner committed
226
227
            break;
    }
228
229
230
231

    // 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.
Michal Vaner's avatar
Michal Vaner committed
232
    if (port != 0xffff) {
Michal Vaner's avatar
Michal Vaner committed
233
        errno = 0;
Michal Vaner's avatar
Michal Vaner committed
234
        if (port == 0xbbbb) {
235
            return (-2);
Michal Vaner's avatar
Michal Vaner committed
236
        } else if (port == 0xcccc) {
237
            return (-1);
Michal Vaner's avatar
Michal Vaner committed
238
        } else {
239
            result |= 0x10;
Michal Vaner's avatar
Michal Vaner committed
240
241
        }
    }
242
    return (result);
Michal Vaner's avatar
Michal Vaner committed
243
244
}

245
// Dummy send function - return data (the result of getSock()) to the destination.
Michal Vaner's avatar
Michal Vaner committed
246
int
247
send_FdDummy(const int destination, const int what) {
248
249
250
251
252
253
    // 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 bute, 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);
Michal Vaner's avatar
Michal Vaner committed
254
255
}

256
257
// Just ignore the fd and pretend success. We close invalid fds in the tests.
int
258
closeIgnore(int) {
259
260
261
    return (0);
}

262
263
264
265
// 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.
266
267
268
269
270
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)
Michal Vaner's avatar
Michal Vaner committed
271
{
272
273
    // Prepare the input feeder and output checker processes.  The feeder
    // process sends data from the client to run() and the checker process
Stephen Morris's avatar
Stephen Morris committed
274
    // reads the response and checks it against the expected response.
275
    int input_fd = 0;
276
    const pid_t input = provide_input(&input_fd, input_data, input_size);
Michal Vaner's avatar
Michal Vaner committed
277
    ASSERT_NE(-1, input) << "Couldn't start input feeder";
278
279

    int output_fd = 0;
280
    const pid_t output = check_output(&output_fd, output_data, output_size);
Michal Vaner's avatar
Michal Vaner committed
281
    ASSERT_NE(-1, output) << "Couldn't start output checker";
282

Michal Vaner's avatar
Michal Vaner committed
283
    // Run the body
Stephen Morris's avatar
Stephen Morris committed
284
    if (should_succeed) {
285
        EXPECT_NO_THROW(run(input_fd, output_fd, getSockDummy, send_fd,
Stephen Morris's avatar
Stephen Morris committed
286
287
                            test_close));
    } else {
288
        EXPECT_THROW(run(input_fd, output_fd, getSockDummy, send_fd,
Stephen Morris's avatar
Stephen Morris committed
289
290
291
                         test_close), isc::socket_creator::SocketCreatorError);
    }

Michal Vaner's avatar
Michal Vaner committed
292
293
294
    // Close the pipes
    close(input_fd);
    close(output_fd);
295

Michal Vaner's avatar
Michal Vaner committed
296
297
298
299
300
    // Check the subprocesses say everything is OK too
    EXPECT_TRUE(process_ok(input));
    EXPECT_TRUE(process_ok(output));
}

301
302

// Check it terminates successfully when asked to.
Michal Vaner's avatar
Michal Vaner committed
303
TEST(run, terminate) {
304
    runTest("T", 1, NULL, 0);
Michal Vaner's avatar
Michal Vaner committed
305
306
}

307
// Check it rejects incorrect input.
Michal Vaner's avatar
Michal Vaner committed
308
TEST(run, bad_input) {
309
    runTest("XXX", 3, "FI", 2, false);
Michal Vaner's avatar
Michal Vaner committed
310
311
}

312
// Check it correctly parses query stream to create sockets.
Michal Vaner's avatar
Michal Vaner committed
313
TEST(run, sockets) {
314
    runTest(
315
316
317
318
319
320
321
322
323
        // 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
324
        "S\x07S\x05S\x0dS\x0f", // Response ("S" + LS byte of getSock() return)
325
        8);                     // Length of response
Michal Vaner's avatar
Michal Vaner committed
326
327
}

328
329

// Check if failures of get_socket are handled correctly.
Michal Vaner's avatar
Michal Vaner committed
330
TEST(run, bad_sockets) {
331
332
333
334
335
336
337
338
    // 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));
Michal Vaner's avatar
Michal Vaner committed
339
    strcpy(result, "EB");
340
341
    strcpy(result + 2 + sizeof(int), "ES");

Michal Vaner's avatar
Michal Vaner committed
342
    // Run the test
343
    runTest(
344
345
346
347
348
        "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));
Michal Vaner's avatar
Michal Vaner committed
349
350
}

351
// A close that fails.  (This causes an abort.)
352
int
353
closeFail(int) {
354
355
356
357
    return (-1);
}

TEST(run, cant_close) {
358
359
360
    runTest("SU4\xff\xff\0\0\0\0", 9,
            "S\x07", 2,
            false, closeFail);
361
362
}

363
364
365
// 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.
366
int
367
sendFDFail(const int, const int) {
368
369
370
371
    return (FD_SYSTEM_ERROR);
}

TEST(run, cant_send_fd) {
372
373
374
    runTest("SU4\xff\xff\0\0\0\0", 9,
            "S", 1,
            false, closeIgnore, sendFDFail);
375
376
}

377
}   // Anonymous namespace