sockcreator_tests.cc 8.11 KB
Newer Older
1
// Copyright (C) 2011  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>

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

using namespace isc::socket_creator;
Michal Vaner's avatar
Michal Vaner committed
29
30
using namespace isc::util::unittests;
using namespace isc::util::io;
Michal Vaner's avatar
Michal Vaner committed
31
32
33
34
35
36
37
38
39
40
41

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.
 */
42
43
#define TEST_ANY_CREATE(SOCK_TYPE, ADDR_TYPE, ADDR_FAMILY, FAMILY_FIELD, \
    ADDR_SET, CHECK_SOCK) \
Michal Vaner's avatar
Michal Vaner committed
44
45
46
47
48
49
50
51
    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); \
52
        addr.FAMILY_FIELD = ADDR_FAMILY; \
Michal Vaner's avatar
Michal Vaner committed
53
54
55
56
57
58
59
60
61
62
        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)); \
Michal Vaner's avatar
Michal Vaner committed
63
    } while (0)
Michal Vaner's avatar
Michal Vaner committed
64
65

// Just helper macros
Michal Vaner's avatar
Michal Vaner committed
66
67
#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)
Michal Vaner's avatar
Michal Vaner committed
68
69
70
// If the get_sock returned something useful, listen must work
#define TCP_CHECK(UNUSED, SOCKET) do { \
        EXPECT_EQ(0, listen(SOCKET, 1)); \
Michal Vaner's avatar
Michal Vaner committed
71
    } while (0)
Michal Vaner's avatar
Michal Vaner committed
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// 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); \
Michal Vaner's avatar
Michal Vaner committed
86
    } while (0)
Michal Vaner's avatar
Michal Vaner committed
87
88
89
90
91

/*
 * Several tests to ensure we can create the sockets.
 */
TEST(get_sock, udp4_create) {
92
93
    TEST_ANY_CREATE(SOCK_DGRAM, sockaddr_in, AF_INET, sin_family, INADDR_SET,
        UDP_CHECK);
Michal Vaner's avatar
Michal Vaner committed
94
95
96
}

TEST(get_sock, tcp4_create) {
97
98
    TEST_ANY_CREATE(SOCK_STREAM, sockaddr_in, AF_INET, sin_family, INADDR_SET,
        TCP_CHECK);
Michal Vaner's avatar
Michal Vaner committed
99
100
101
}

TEST(get_sock, udp6_create) {
102
103
    TEST_ANY_CREATE(SOCK_DGRAM, sockaddr_in6, AF_INET6, sin6_family,
        IN6ADDR_SET, UDP_CHECK);
Michal Vaner's avatar
Michal Vaner committed
104
105
106
}

TEST(get_sock, tcp6_create) {
107
108
    TEST_ANY_CREATE(SOCK_STREAM, sockaddr_in6, AF_INET6, sin6_family,
        IN6ADDR_SET, TCP_CHECK);
Michal Vaner's avatar
Michal Vaner committed
109
110
111
112
113
114
115
}

/*
 * Try to ask the get_sock function some nonsense and test if it
 * is able to report error.
 */
TEST(get_sock, fail_with_nonsense) {
Michal Vaner's avatar
Michal Vaner committed
116
117
118
    struct sockaddr addr;
    memset(&addr, 0, sizeof addr);
    ASSERT_LT(get_sock(0, &addr, sizeof addr), 0);
Michal Vaner's avatar
Michal Vaner committed
119
120
}

Michal Vaner's avatar
Michal Vaner committed
121
122
123
124
/*
 * Helper functions to pass to run during testing.
 */
int
Michal Vaner's avatar
Michal Vaner committed
125
get_sock_dummy(const int type, struct sockaddr *addr, const socklen_t)
Michal Vaner's avatar
Michal Vaner committed
126
127
{
    int result(0);
Michal Vaner's avatar
Michal Vaner committed
128
    int port(0);
Michal Vaner's avatar
Michal Vaner committed
129
130
131
132
133
134
135
    /*
     * 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.
     */
Michal Vaner's avatar
Michal Vaner committed
136
    switch (type) {
Michal Vaner's avatar
Michal Vaner committed
137
138
139
140
141
142
143
        case SOCK_STREAM:
            result += 1;
            break;
        case SOCK_DGRAM:
            result += 3;
            break;
    }
Michal Vaner's avatar
Michal Vaner committed
144
    switch (addr->sa_family) {
Michal Vaner's avatar
Michal Vaner committed
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
        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.
     */
Michal Vaner's avatar
Michal Vaner committed
161
    if (port != 0xffff) {
Michal Vaner's avatar
Michal Vaner committed
162
        errno = 0;
Michal Vaner's avatar
Michal Vaner committed
163
        if (port == 0xbbbb) {
Michal Vaner's avatar
Michal Vaner committed
164
            return -2;
Michal Vaner's avatar
Michal Vaner committed
165
        } else if (port == 0xcccc) {
Michal Vaner's avatar
Michal Vaner committed
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
            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);
Michal Vaner's avatar
Michal Vaner committed
182
    if (!write_data(destination, &fd_data, 1)) {
Michal Vaner's avatar
Michal Vaner committed
183
        return -1;
Michal Vaner's avatar
Michal Vaner committed
184
185
186
    } else {
        return 0;
    }
Michal Vaner's avatar
Michal Vaner committed
187
188
189
190
191
192
193
194
195
196
197
198
199
}

/*
 * 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
Michal Vaner's avatar
Michal Vaner committed
200
    int input_fd(0), output_fd(0);
Michal Vaner's avatar
Michal Vaner committed
201
202
203
204
205
206
    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));
Michal Vaner's avatar
Michal Vaner committed
207
208
209
210
    // Close the pipes
    close(input_fd);
    close(output_fd);
    // Did it run well?
Michal Vaner's avatar
Michal Vaner committed
211
    if (should_succeed) {
Michal Vaner's avatar
Michal Vaner committed
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
        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);
}

Michal Vaner's avatar
Michal Vaner committed
269
}