1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
|
// 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
|