sockcreator.cc 7.21 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);
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
    }
}

} // Anonymous namespace

191
192
namespace isc {
namespace socket_creator {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
193

194
195
// Get the socket and bind to it.
int
196
getSock(const int type, struct sockaddr* bind_addr, const socklen_t addr_len) {
197
    const int sock = socket(bind_addr->sa_family, type, 0);
198
    if (sock == -1) {
199
        return (-1);
200
    }
Stephen Morris's avatar
Stephen Morris committed
201
    const int on = 1;
202
    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
203
204
        // This is part of the binding process, so it's a bind error
        return (-2);
205
206
207
    }
    if (bind_addr->sa_family == AF_INET6 &&
        setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1) {
208
209
        // This is part of the binding process, so it's a bind error
        return (-2);
210
211
    }
    if (bind(sock, bind_addr, addr_len) == -1) {
212
        return (-2);
213
    }
214
    return (sock);
215
216
217
}

// Main run loop.
Stephen Morris's avatar
Stephen Morris committed
218
void
219
220
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
221
{
Michal Vaner's avatar
Michal Vaner committed
222
223
    for (;;) {
        char command;
224
        readMessage(input_fd, &command, sizeof(command));
Michal Vaner's avatar
Michal Vaner committed
225
        switch (command) {
226
            case 'S':   // The "get socket" command
227
228
                handleRequest(input_fd, output_fd, get_sock,
                              send_fd_fun, close_fun);
Michal Vaner's avatar
Michal Vaner committed
229
                break;
230

231
232
233
234
            case 'T':   // The "terminate" command
                return;

            default:    // Don't recognise anything else
235
                protocolError(output_fd);
Michal Vaner's avatar
Michal Vaner committed
236
237
        }
    }
238
239
240
241
}

} // End of the namespaces
}