Branch data Line data Source code
1 : : // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
2 : : //
3 : : // Permission to use, copy, modify, and/or distribute this software for any
4 : : // purpose with or without fee is hereby granted, provided that the above
5 : : // copyright notice and this permission notice appear in all copies.
6 : : //
7 : : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
8 : : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9 : : // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
10 : : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11 : : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
12 : : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13 : : // PERFORMANCE OF THIS SOFTWARE.
14 : :
15 : : #ifndef __SOCKETSESSION_H_
16 : : #define __SOCKETSESSION_H_ 1
17 : :
18 : : #include <string>
19 : :
20 : : #include <boost/noncopyable.hpp>
21 : :
22 : : #include <exceptions/exceptions.h>
23 : :
24 : : namespace isc {
25 : : namespace util {
26 : : namespace io {
27 : :
28 : : /// \page SocketSessionUtility Socket session utility
29 : : ///
30 : : /// This utility defines a set of classes that support forwarding a
31 : : /// "socket session" from one process to another. A socket session is a
32 : : /// conceptual tuple of the following elements:
33 : : /// - A network socket
34 : : /// - The local and remote endpoints of a (IP) communication taking place on
35 : : /// the socket. In practice an endpoint is a pair of an IP address and
36 : : /// TCP or UDP port number.
37 : : /// - Some amount of data sent from the remote endpoint and received on the
38 : : /// socket. We call it (socket) session data in this documentation.
39 : : ///
40 : : /// Note that this is a conceptual definition. Depending on the underlying
41 : : /// implementation and/or the network protocol, some of the elements could be
42 : : /// part of others; for example, if it's an established TCP connection,
43 : : /// the local and remote endpoints would be able to be retrieved from the
44 : : /// socket using the standard \c getsockname() and \c getpeername() system
45 : : /// calls. But in this definition we separate these to be more generic.
46 : : /// Also, as a matter of fact our intended usage includes non-connected UDP
47 : : /// communications, in which case at least the remote endpoint should be
48 : : /// provided separately from the socket.
49 : : ///
50 : : /// In the actual implementation we represent a socket as a tuple of
51 : : /// socket's file descriptor, address family (e.g. \c AF_INET6),
52 : : /// socket type (e.g. \c SOCK_STREAM), and protocol (e.g. \c IPPROTO_TCP).
53 : : /// The latter three are included in the representation of a socket in order
54 : : /// to provide complete information of how the socket would be created
55 : : /// by the \c socket(2) system call. More specifically in practice, these
56 : : /// parameters could be used to construct a Python socket object from the
57 : : /// file descriptor.
58 : : ///
59 : : /// We use the standard \c sockaddr structure to represent endpoints.
60 : : ///
61 : : /// Socket session data is an opaque memory region of an arbitrary length
62 : : /// (possibly with some reasonable upper limit).
63 : : ///
64 : : /// To forward a socket session between processes, we use connected UNIX
65 : : /// domain sockets established between the processes. The file descriptor
66 : : /// will be forwarded through the sockets as an ancillary data item of
67 : : /// type \c SCM_RIGHTS. Other elements of the session will be transferred
68 : : /// as normal data over the connection.
69 : : ///
70 : : /// We provide three classes to help applications forward socket sessions:
71 : : /// \c SocketSessionForwarder is the sender of the UNIX domain connection,
72 : : /// while \c SocketSessionReceiver is the receiver (this interface assumes
73 : : /// one direction of forwarding); \c SocketSession represents a single
74 : : /// socket session.
75 : : ///
76 : : /// \c SocketSessionForwarder and \c SocketSessionReceiver objects use a
77 : : /// straightforward protocol to pass elements of socket sessions.
78 : : /// Once the connection is established, the forwarder object first forwards
79 : : /// the file descriptor with 1-byte dummy data. It then forwards a
80 : : /// "(socket) session header", which contains all other elements of the session
81 : : /// except the file descriptor (already forwarded) and session data.
82 : : /// The wire format of the header is as follows:
83 : : /// - The length of the header (16-bit unsigned integer)
84 : : /// - Address family
85 : : /// - Socket type
86 : : /// - Protocol
87 : : /// - Size of the local endpoint in bytes
88 : : /// - Local endpoint (a copy of the memory image of the corresponding
89 : : /// \c sockaddr)
90 : : /// - Size of the remote endpoint in bytes
91 : : /// - Remote endpoint (same as local endpoint)
92 : : /// - Size of session data in bytes
93 : : ///
94 : : /// The type of the fields is 32-bit unsigned integer unless explicitly
95 : : /// noted, and all fields are formatted in the network byte order.
96 : : ///
97 : : /// The socket session data immediately follows the session header.
98 : : ///
99 : : /// Note that the fields do not necessarily be in the network byte order
100 : : /// because they are expected to be exchanged on the same machine. Likewise,
101 : : /// integer elements such as address family do not necessarily be represented
102 : : /// as an fixed-size value (i.e., 32-bit). But fixed size fields are used
103 : : /// in order to ensure maximum portability in such a (rare) case where the
104 : : /// forwarder and the receiver are built with different compilers that have
105 : : /// different definitions of \c int. Also, since \c sockaddr fields are
106 : : /// generally formatted in the network byte order, other fields are defined
107 : : /// so to be consistent.
108 : : ///
109 : : /// One basic assumption in the API of this utility is socket sessions should
110 : : /// be forwarded without blocking, thus eliminating the need for incremental
111 : : /// read/write or blocking other important services such as responding to
112 : : /// requests from the application's clients. This assumption should be held
113 : : /// as long as both the forwarder and receiver have sufficient resources
114 : : /// to handle the forwarding process since the communication is local.
115 : : /// But a forward attempt could still block if the receiver is busy (or even
116 : : /// hang up) and cannot keep up with the volume of incoming sessions.
117 : : ///
118 : : /// So, in this implementation, the forwarder uses non blocking writes to
119 : : /// forward sessions. If a write attempt could block, it immediately gives
120 : : /// up the operation with an exception. The corresponding application is
121 : : /// expected to catch it, close the connection, and perform any necessary
122 : : /// recovery for that application (that would normally be re-establish the
123 : : /// connection with a new receiver, possibly after confirming the receiving
124 : : /// side is still alive). On the other hand, the receiver implementation
125 : : /// assumes it's possible that it only receive incomplete elements of a
126 : : /// session (such as in the case where the forwarder writes part of the
127 : : /// entire session and gives up the connection). The receiver implementation
128 : : /// throws an exception when it encounters an incomplete session. Like the
129 : : /// case of the forwarder application, the receiver application is expected
130 : : /// to catch it, close the connection, and perform any necessary recovery
131 : : /// steps.
132 : : ///
133 : : /// Note that the receiver implementation uses blocking read. So it's
134 : : /// application's responsibility to ensure that there's at least some data
135 : : /// in the connection when the receiver object is requested to receive a
136 : : /// session (unless this operation can be blocking, e.g., by the use of
137 : : /// a separate thread). Also, if the forwarder implementation or application
138 : : /// is malicious or extremely buggy and intentionally sends partial session
139 : : /// and keeps the connection, the receiver could block in receiving a session.
140 : : /// In general, we assume the forwarder doesn't do intentional blocking
141 : : /// as it's a local node and is generally a module of the same (BIND 10)
142 : : /// system. The minimum requirement for the forwarder implementation (and
143 : : /// application) is to make sure the connection is closed once it detects
144 : : /// an error on it. Even a naive implementation that simply dies due to
145 : : /// the exception will meet this requirement.
146 : :
147 : : /// An exception indicating general errors that takes place in the
148 : : /// socket session related class objects.
149 : : ///
150 : : /// In general the errors are unusual but possible failures such as unexpected
151 : : /// connection reset, and suggest the application to close the connection and
152 : : /// (if necessary) reestablish it.
153 : 24 : class SocketSessionError: public Exception {
154 : : public:
155 : 24 : SocketSessionError(const char *file, size_t line, const char *what):
156 : 24 : isc::Exception(file, line, what) {}
157 : : };
158 : :
159 : : /// The forwarder of socket sessions
160 : : ///
161 : : /// An object of this class maintains a UNIX domain socket (normally expected
162 : : /// to be connected to a \c SocketSessionReceiver object) and forwards
163 : : /// socket sessions to the receiver.
164 : : ///
165 : : /// See the description of \ref SocketSessionUtility for other details of how
166 : : /// the session forwarding works.
167 : : class SocketSessionForwarder : boost::noncopyable {
168 : : public:
169 : : /// The constructor.
170 : : ///
171 : : /// It's constructed with path information of the intended receiver,
172 : : /// but does not immediately establish a connection to the receiver;
173 : : /// \c connectToReceiver() must be called to establish it. These are
174 : : /// separated so that an object of class can be initialized (possibly
175 : : /// as an attribute of a higher level application class object) without
176 : : /// knowing the receiver is ready for accepting new forwarders. The
177 : : /// separate connect interface allows the object to be reused when it
178 : : /// detects connection failure and tries to re-establish it after closing
179 : : /// the failed one.
180 : : ///
181 : : /// On construction, it also installs a signal filter for SIGPIPE to
182 : : /// ignore it. Since this class uses a stream-type connected UNIX domain
183 : : /// socket, if the receiver (abruptly) closes the connection a subsequent
184 : : /// write operation on the socket would trigger a SIGPIPE signal, which
185 : : /// kills the caller process by default. This behavior would be
186 : : /// undesirable in many cases, so this implementation always disables
187 : : /// the signal.
188 : : ///
189 : : /// This approach has some drawbacks, however; first, since signal handling
190 : : /// is process (or thread) wide, ignoring it may not what the application
191 : : /// wants. On the other hand, if the application changes how the signal is
192 : : /// handled after instantiating this class, the new behavior affects the
193 : : /// class operation. Secondly, even if ignoring the signal is the desired
194 : : /// operation, it's a waste to set the filter every time this class object
195 : : /// is constructed. It's sufficient to do it once. We still adopt this
196 : : /// behavior based on the observation that in most cases applications would
197 : : /// like to ignore SIGPIPE (or simply doesn't care about it) and that this
198 : : /// class is not instantiated so often (so the wasteful setting overhead
199 : : /// should be marginal). On the other hand, doing it every time is
200 : : /// beneficial if the application is threaded and different threads
201 : : /// create different forwarder objects (and if signals work per thread).
202 : : ///
203 : : /// \exception SocketSessionError \c unix_file is invalid as a path name
204 : : /// of a UNIX domain socket.
205 : : /// \exception Unexpected Error in setting a filter for SIGPIPE (see above)
206 : : /// \exception std::bad_alloc resource allocation failure
207 : : ///
208 : : /// \param unix_file Path name of the receiver.
209 : : explicit SocketSessionForwarder(const std::string& unix_file);
210 : :
211 : : /// The destructor.
212 : : ///
213 : : /// If a connection has been established, it's automatically closed in
214 : : /// the destructor.
215 : : ~SocketSessionForwarder();
216 : :
217 : : /// Establish a connection to the receiver.
218 : : ///
219 : : /// This method establishes a connection to the receiver at the path
220 : : /// given on construction. It makes the underlying UNIX domain socket
221 : : /// non blocking, so this method (or subsequent \c push() calls) does not
222 : : /// block.
223 : : ///
224 : : /// \exception BadValue The method is called while an already
225 : : /// established connection is still active.
226 : : /// \exception SocketSessionError A system error in socket operation.
227 : : void connectToReceiver();
228 : :
229 : : /// Close the connection to the receiver.
230 : : ///
231 : : /// The connection must have been established by \c connectToReceiver().
232 : : /// As long as it's met this method is exception free.
233 : : ///
234 : : /// \exception BadValue The connection hasn't been established.
235 : : void close();
236 : :
237 : : /// Forward a socket session to the receiver.
238 : : ///
239 : : /// This method takes a set of parameters that represent a single socket
240 : : /// session, renders them in the "wire" format according to the internal
241 : : /// protocol (see \ref SocketSessionUtility) and forwards them to
242 : : /// the receiver through the UNIX domain connection.
243 : : ///
244 : : /// The connection must have been established by \c connectToReceiver().
245 : : ///
246 : : /// For simplicity and for the convenience of detecting application
247 : : /// errors, this method imposes some restrictions on the parameters:
248 : : /// - Socket family must be either \c AF_INET or \c AF_INET6
249 : : /// - The address family (\c sa_family) member of the local and remote
250 : : /// end points must be equal to the \c family parameter
251 : : /// - Socket session data must not be empty (\c data_len must not be 0
252 : : /// and \c data must not be NULL)
253 : : /// - Data length must not exceed 65535
254 : : /// These are not architectural limitation, and might be loosened in
255 : : /// future versions as we see the need for flexibility.
256 : : ///
257 : : /// Since the underlying UNIX domain socket is non blocking
258 : : /// (see the description for the constructor), a call to this method
259 : : /// should either return immediately or result in exception (in case of
260 : : /// "would block").
261 : : ///
262 : : /// \exception BadValue The method is called before establishing a
263 : : /// connection or given parameters are invalid.
264 : : /// \exception SocketSessionError A system error in socket operation,
265 : : /// including the case where the write operation would block.
266 : : ///
267 : : /// \param sock The socket file descriptor
268 : : /// \param family The address family (such as AF_INET6) of the socket
269 : : /// \param type The socket type (such as SOCK_DGRAM) of the socket
270 : : /// \param protocol The transport protocol (such as IPPROTO_UDP) of the
271 : : /// socket
272 : : /// \param local_end The local end point of the session in the form of
273 : : /// \c sockaddr.
274 : : /// \param remote_end The remote end point of the session in the form of
275 : : /// \c sockaddr.
276 : : /// \param data A pointer to the beginning of the memory region for the
277 : : /// session data
278 : : /// \param data_len The size of the session data in bytes.
279 : : void push(int sock, int family, int type, int protocol,
280 : : const struct sockaddr& local_end,
281 : : const struct sockaddr& remote_end,
282 : : const void* data, size_t data_len);
283 : :
284 : : private:
285 : : struct ForwarderImpl;
286 : : ForwarderImpl* impl_;
287 : : };
288 : :
289 : : /// Socket session object.
290 : : ///
291 : : /// The \c SocketSession class provides a convenient encapsulation
292 : : /// for the notion of a socket session. It's instantiated with straightforward
293 : : /// parameters corresponding to a socket session, and provides read only
294 : : /// accessors to the parameters to ensure data integrity.
295 : : ///
296 : : /// In the initial design and implementation it's only used as a return type
297 : : /// of \c SocketSessionReceiver::pop(), but it could also be used by
298 : : /// the \c SocketSessionForwarder class or for other purposes.
299 : : ///
300 : : /// It is assumed that the original owner of a \c SocketSession object
301 : : /// (e.g. a class or a function that constructs it) is responsible for validity
302 : : /// of the data passed to the object. See the description of
303 : : /// \c SocketSessionReceiver::pop() for the specific case of that usage.
304 : : class SocketSession {
305 : : public:
306 : : /// The constructor.
307 : : ///
308 : : /// This is a trivial constructor, taking a straightforward representation
309 : : /// of session parameters and storing them internally to ensure integrity.
310 : : ///
311 : : /// As long as the given parameters are valid it never throws an exception.
312 : : ///
313 : : /// \exception BadValue Given parameters don't meet the requirement
314 : : /// (see the parameter descriptions).
315 : : ///
316 : : /// \param sock The socket file descriptor
317 : : /// \param family The address family (such as AF_INET6) of the socket
318 : : /// \param type The socket type (such as SOCK_DGRAM) of the socket
319 : : /// \param protocol The transport protocol (such as IPPROTO_UDP) of the
320 : : /// socket.
321 : : /// \param local_end The local end point of the session in the form of
322 : : /// \c sockaddr. Must not be NULL.
323 : : /// \param remote_end The remote end point of the session in the form of
324 : : /// \c sockaddr. Must not be NULL.
325 : : /// \param data A pointer to the beginning of the memory region for the
326 : : /// session data. Must not be NULL, and the subsequent \c data_len bytes
327 : : /// must be valid.
328 : : /// \param data_len The size of the session data in bytes. Must not be 0.
329 : : SocketSession(int sock, int family, int type, int protocol,
330 : : const sockaddr* local_end, const sockaddr* remote_end,
331 : : const void* data, size_t data_len);
332 : :
333 : : /// Return the socket file descriptor.
334 : 0 : int getSocket() const { return (sock_); }
335 : :
336 : : /// Return the address family (such as AF_INET6) of the socket.
337 : 0 : int getFamily() const { return (family_); }
338 : :
339 : : /// Return the socket type (such as SOCK_DGRAM) of the socket.
340 : 0 : int getType() const { return (type_); }
341 : :
342 : : /// Return the transport protocol (such as IPPROTO_UDP) of the socket.
343 : 0 : int getProtocol() const { return (protocol_); }
344 : :
345 : : /// Return the local end point of the session in the form of \c sockaddr.
346 : 0 : const sockaddr& getLocalEndpoint() const { return (*local_end_); }
347 : :
348 : : /// Return the remote end point of the session in the form of \c sockaddr.
349 : 0 : const sockaddr& getRemoteEndpoint() const { return (*remote_end_); }
350 : :
351 : : /// Return a pointer to the beginning of the memory region for the session
352 : : /// data.
353 : : ///
354 : : /// In the current implementation it should never be NULL, and the region
355 : : /// of the size returned by \c getDataLength() is expected to be valid.
356 : 0 : const void* getData() const { return (data_); }
357 : :
358 : : /// Return the size of the session data in bytes.
359 : : ///
360 : : /// In the current implementation it should be always larger than 0.
361 : 0 : size_t getDataLength() const { return (data_len_); }
362 : :
363 : : private:
364 : : const int sock_;
365 : : const int family_;
366 : : const int type_;
367 : : const int protocol_;
368 : : const sockaddr* local_end_;
369 : : const sockaddr* remote_end_;
370 : : const void* const data_;
371 : : const size_t data_len_;
372 : : };
373 : :
374 : : /// The receiver of socket sessions
375 : : ///
376 : : /// An object of this class holds a UNIX domain socket for an
377 : : /// <em>established connection</em>, receives socket sessions from
378 : : /// the remote forwarder, and provides the session to the application
379 : : /// in the form of a \c SocketSession object.
380 : : ///
381 : : /// Note that this class is instantiated with an already connected socket;
382 : : /// it's not a listening socket that is accepting connection requests from
383 : : /// forwarders. It's application's responsibility to create the listening
384 : : /// socket, listen on it and accept connections. Once the connection is
385 : : /// established, the application would construct a \c SocketSessionReceiver
386 : : /// object with the socket for the newly established connection.
387 : : /// This behavior is based on the design decision that the application should
388 : : /// decide when it performs (possibly) blocking operations (see \ref
389 : : /// SocketSessionUtility for more details).
390 : : ///
391 : : /// See the description of \ref SocketSessionUtility for other details of how
392 : : /// the session forwarding works.
393 : : class SocketSessionReceiver : boost::noncopyable {
394 : : public:
395 : : /// The constructor.
396 : : ///
397 : : /// \exception SocketSessionError Any error on an operation that is
398 : : /// performed on the given socket as part of initialization.
399 : : /// \exception std::bad_alloc Resource allocation failure
400 : : ///
401 : : /// \param fd A UNIX domain socket for an established connection with
402 : : /// a forwarder.
403 : : explicit SocketSessionReceiver(int fd);
404 : :
405 : : /// The destructor.
406 : : ///
407 : : /// The destructor does \c not close the socket given on construction.
408 : : /// It's up to the application what to do with it (note that the
409 : : /// application would have to maintain the socket itself for detecting
410 : : /// the existence of a new socket session asynchronously).
411 : : ~SocketSessionReceiver();
412 : :
413 : : /// Receive a socket session from the forwarder.
414 : : ///
415 : : /// This method receives wire-format data (see \ref SocketSessionUtility)
416 : : /// for a socket session on the UNIX domain socket, performs some
417 : : /// validation on the data, and returns the session information in the
418 : : /// form of a \c SocketSession object.
419 : : ///
420 : : /// The returned SocketSession object is valid only until the next time
421 : : /// this method is called or until the \c SocketSessionReceiver object is
422 : : /// destructed.
423 : : ///
424 : : /// The caller is responsible for closing the received socket (whose
425 : : /// file descriptor is accessible via \c SocketSession::getSocket()).
426 : : /// If the caller copies the returned \c SocketSession object, it's also
427 : : /// responsible for making sure the descriptor is closed at most once.
428 : : /// On the other hand, the caller is not responsible for freeing the
429 : : /// socket session data (accessible via \c SocketSession::getData());
430 : : /// the \c SocketSessionReceiver object will clean it up automatically.
431 : : ///
432 : : /// It ensures the following:
433 : : /// - The address family is either \c AF_INET or \c AF_INET6
434 : : /// - The address family (\c sa_family) member of the local and remote
435 : : /// end points must be equal to the \c family parameter
436 : : /// - The socket session data is not empty and does not exceed 65535
437 : : /// bytes.
438 : : /// If the validation fails or an unexpected system error happens
439 : : /// (including a connection close in the meddle of reception), it throws
440 : : /// an SocketSessionError exception. When this happens, it's very
441 : : /// unlikely that a subsequent call to this method succeeds, so in reality
442 : : /// the application is expected to destruct it and close the socket in
443 : : /// such a case.
444 : : ///
445 : : /// \exception SocketSessionError Invalid data is received or a system
446 : : /// error on socket operation happens.
447 : : /// \exception std::bad_alloc Resource allocation failure
448 : : ///
449 : : /// \return A \c SocketSession object corresponding to the extracted
450 : : /// socket session.
451 : : SocketSession pop();
452 : :
453 : : private:
454 : : struct ReceiverImpl;
455 : : ReceiverImpl* impl_;
456 : : };
457 : :
458 : : }
459 : : }
460 : : }
461 : :
462 : : #endif // __SOCKETSESSION_H_
463 : :
464 : : // Local Variables:
465 : : // mode: c++
466 : : // End:
|