sockcreator.cc 8.92 KB
Newer Older
1
// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
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
#include <util/io/fd.h>
18
#include <util/io/sockaddr_util.h>
Michal Vaner's avatar
Michal Vaner committed
19

Michal Vaner's avatar
Michal Vaner committed
20
#include <cerrno>
21
#include <string.h>
22 23

#include <unistd.h>
Michal Vaner's avatar
Michal Vaner committed
24 25 26
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
Michal Vaner's avatar
Michal Vaner committed
27

Michal Vaner's avatar
Michal Vaner committed
28
using namespace isc::util::io;
29
using namespace isc::util::io::internal;
30
using namespace isc::socket_creator;
31

32 33 34
namespace {

// Simple wrappers for read_data/write_data that throw an exception on error.
Stephen Morris's avatar
Stephen Morris committed
35
void
36
readMessage(const int fd, void* where, const size_t length) {
Stephen Morris's avatar
Stephen Morris committed
37 38 39 40 41 42
    if (read_data(fd, where, length) < length) {
        isc_throw(ReadError, "Error reading from socket creator client");
    }
}

void
43
writeMessage(const int fd, const void* what, const size_t length) {
Stephen Morris's avatar
Stephen Morris committed
44 45 46 47 48
    if (!write_data(fd, what, length)) {
        isc_throw(WriteError, "Error writing to socket creator client");
    }
}

49
// Exit on a protocol error after informing the client of the problem.
Stephen Morris's avatar
Stephen Morris committed
50
void
51
protocolError(const int fd, const char reason = 'I') {
52

Stephen Morris's avatar
Stephen Morris committed
53 54 55 56
    // Tell client we have a problem
    char message[2];
    message[0] = 'F';
    message[1] = reason;
57
    writeMessage(fd, message, sizeof(message));
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
58

Stephen Morris's avatar
Stephen Morris committed
59 60 61
    // ... and exit
    isc_throw(ProtocolError, "Fatal error, reason: " << reason);
}
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
62

63 64
// Return appropriate socket type constant for the socket type requested.
// The output_fd argument is required to report a protocol error.
JINMEI Tatuya's avatar
JINMEI Tatuya committed
65 66
int
getSocketType(const char type_code, const int output_fd) {
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
    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.
JINMEI Tatuya's avatar
JINMEI Tatuya committed
85 86
char
getErrorCode(const int status) {
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
    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);
}


104 105
// Handle the request from the client.
//
106 107
// Reads the type and family of socket required, creates the socket and returns
// it to the client.
108
//
109 110
// The arguments passed (and the exceptions thrown) are the same as those for
// run().
111
void
112 113 114
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)
115
{
116
    // Read the message from the client
117
    char type[2];
118
    readMessage(input_fd, type, sizeof(type));
119 120

    // Decide what type of socket is being asked for
121
    const int sock_type = getSocketType(type[0], output_fd);
122 123 124 125 126 127 128

    // 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;
129
    switch (type[1]) { // The address family
130

131 132
        // The casting to apparently incompatible types is required by the
        // C low-level interface.
133

134
        case '4':
135
            addr = convertSockAddr(&addr_in);
136
            addr_len = sizeof(addr_in);
137
            memset(&addr_in, 0, sizeof(addr_in));
138
            addr_in.sin_family = AF_INET;
139 140 141
            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));
142
            break;
143

144
        case '6':
145 146
            addr = convertSockAddr(&addr_in6);
            addr_len = sizeof(addr_in6);
147
            memset(&addr_in6, 0, sizeof(addr_in6));
148
            addr_in6.sin6_family = AF_INET6;
149 150 151 152
            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));
153
            break;
154

155
        default:
156
            protocolError(output_fd);
157
    }
158 159

    // Obtain the socket
160
    const int result = get_sock(sock_type, addr, addr_len, close_fun);
161 162
    if (result >= 0) {
        // Got the socket, send it to the client.
163
        writeMessage(output_fd, "S", 1);
164
        if (send_fd_fun(output_fd, result) != 0) {
165 166
            // Error.  Close the socket (ignore any error from that operation)
            // and abort.
167 168 169
            close_fun(result);
            isc_throw(InternalError, "Error sending descriptor");
        }
170

171 172
        // Successfully sent the socket, so free up resources we still hold
        // for it.
173 174 175 176
        if (close_fun(result) == -1) {
            isc_throw(InternalError, "Error closing socket");
        }
    } else {
177
        // Error.  Tell the client.
178 179 180 181
        char error_message[2];
        error_message[0] = 'E';
        error_message[1] = getErrorCode(result);
        writeMessage(output_fd, error_message, sizeof(error_message));
182

183
        // ...and append the reason code to the error message
184 185
        const int error_number = errno;
        writeMessage(output_fd, &error_number, sizeof(error_number));
186 187 188
    }
}

189 190 191 192 193 194 195 196 197 198 199 200 201 202
// 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);
    }
203
#else // Try the following as fallback
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
#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);
    }
219
#endif
220 221 222 223
#endif
    return (fd);
}

224 225 226 227 228 229 230 231 232 233 234
// 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);
}

235 236
} // Anonymous namespace

237 238
namespace isc {
namespace socket_creator {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
239

240 241
// Get the socket and bind to it.
int
242 243
getSock(const int type, struct sockaddr* bind_addr, const socklen_t addr_len,
        const close_t close_fun) {
244
    const int sock = socket(bind_addr->sa_family, type, 0);
245
    if (sock == -1) {
246
        return (-1);
247
    }
Stephen Morris's avatar
Stephen Morris committed
248
    const int on = 1;
249
    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
250
        // This is part of the binding process, so it's a bind error
251
        return (maybeClose(-2, sock, close_fun));
252 253 254
    }
    if (bind_addr->sa_family == AF_INET6 &&
        setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1) {
255
        // This is part of the binding process, so it's a bind error
256
        return (maybeClose(-2, sock, close_fun));
257 258
    }
    if (bind(sock, bind_addr, addr_len) == -1) {
259
        return (maybeClose(-2, sock, close_fun));
260
    }
261 262
    if (type == SOCK_DGRAM && bind_addr->sa_family == AF_INET6) {
        // Set some MTU flags on IPv6 UDP sockets.
263
        return (maybeClose(mtu(sock), sock, close_fun));
264
    }
265
    return (sock);
266 267 268
}

// Main run loop.
Stephen Morris's avatar
Stephen Morris committed
269
void
270 271
run(const int input_fd, const int output_fd, get_sock_t get_sock,
    send_fd_t send_fd_fun, close_t close_fun)
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
272
{
Michal Vaner's avatar
Michal Vaner committed
273 274
    for (;;) {
        char command;
275
        readMessage(input_fd, &command, sizeof(command));
Michal Vaner's avatar
Michal Vaner committed
276
        switch (command) {
277
            case 'S':   // The "get socket" command
278 279
                handleRequest(input_fd, output_fd, get_sock,
                              send_fd_fun, close_fun);
Michal Vaner's avatar
Michal Vaner committed
280
                break;
281

282 283 284
            case 'T':   // The "terminate" command
                return;

Francis Dupont's avatar
Francis Dupont committed
285
            default:    // Don't recognize anything else
286
                protocolError(output_fd);
Michal Vaner's avatar
Michal Vaner committed
287 288
        }
    }
289 290 291 292
}

} // End of the namespaces
}