Line data Source code
1 : // Copyright (c) 2012-2019 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // This program is free software; you can redistribute it and/or modify
4 : // it under the terms of the GNU General Public License as published by
5 : // the Free Software Foundation; either version 2 of the License, or
6 : // (at your option) any later version.
7 : //
8 : // This program is distributed in the hope that it will be useful,
9 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 : // GNU General Public License for more details.
12 : //
13 : // You should have received a copy of the GNU General Public License
14 : // along with this program; if not, write to the Free Software
15 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16 :
17 : /** \file
18 : * \brief Implementation of the Snap Communicator class.
19 : *
20 : * This class wraps the C poll() interface in a C++ object with many types
21 : * of objects:
22 : *
23 : * \li Server Connections; for software that want to offer a port to
24 : * which clients can connect to; the server will call accept()
25 : * once a new client connection is ready; this results in a
26 : * Server/Client connection object
27 : * \li Client Connections; for software that want to connect to
28 : * a server; these expect the IP address and port to connect to
29 : * \li Server/Client Connections; for the server when it accepts a new
30 : * connection; in this case the server gets a socket from accept()
31 : * and creates one of these objects to handle the connection
32 : *
33 : * Using the poll() function is the easiest and allows us to listen
34 : * on pretty much any number of sockets (on my server it is limited
35 : * at 16,768 and frankly over 1,000 we probably will start to have
36 : * real slowness issues on small VPN servers.)
37 : */
38 :
39 : // self
40 : //
41 : #include "eventdispatcher/tcp_server_client_connection.h"
42 :
43 : #include "eventdispatcher/exception.h"
44 :
45 :
46 : // snaplogger lib
47 : //
48 : #include "snaplogger/message.h"
49 :
50 :
51 : // C lib
52 : //
53 : #include <arpa/inet.h>
54 : #include <netdb.h>
55 :
56 :
57 : // last include
58 : //
59 : #include <snapdev/poison.h>
60 :
61 :
62 :
63 : namespace ed
64 : {
65 :
66 :
67 :
68 : /** \brief Create a client connection created from an accept().
69 : *
70 : * This constructor initializes a client connection from a socket
71 : * that we received from an accept() call.
72 : *
73 : * The destructor will automatically close that socket on destruction.
74 : *
75 : * \param[in] client The client that acecpt() returned.
76 : */
77 0 : tcp_server_client_connection::tcp_server_client_connection(tcp_bio_client::pointer_t client)
78 0 : : f_client(client)
79 : {
80 0 : }
81 :
82 :
83 : /** \brief Make sure the socket gets released.
84 : *
85 : * This destructor makes sure that the socket gets closed.
86 : */
87 0 : tcp_server_client_connection::~tcp_server_client_connection()
88 : {
89 0 : close();
90 0 : }
91 :
92 :
93 : /** \brief Read data from the TCP server client socket.
94 : *
95 : * This function reads as much data up to the specified amount
96 : * in \p count. The read data is saved in \p buf.
97 : *
98 : * \param[in,out] buf The buffer where the data gets read.
99 : * \param[in] count The maximum number of bytes to read in buf.
100 : *
101 : * \return The number of bytes read or -1 if an error occurred.
102 : */
103 0 : ssize_t tcp_server_client_connection::read(void * buf, size_t count)
104 : {
105 0 : if(!f_client)
106 : {
107 0 : errno = EBADF;
108 0 : return -1;
109 : }
110 0 : return f_client->read(reinterpret_cast<char *>(buf), count);
111 : }
112 :
113 :
114 : /** \brief Write data to this connection's socket.
115 : *
116 : * This function writes up to \p count bytes of data from \p buf
117 : * to this connection's socket.
118 : *
119 : * \warning
120 : * This write function may not always write all the data you are
121 : * trying to send to the remote connection. If you want to make
122 : * sure that all your data is written to the other connection,
123 : * you want to instead use the snap_tcp_server_client_buffer_connection
124 : * which overloads the write() function and saves the data to be
125 : * written to the socket in a buffer. The snap communicator run
126 : * loop is then responsible for sending all the data.
127 : *
128 : * \param[in] buf The buffer of data to be written to the socket.
129 : * \param[in] count The number of bytes the caller wants to write to the
130 : * conneciton.
131 : *
132 : * \return The number of bytes written to the socket or -1 if an error occurred.
133 : */
134 0 : ssize_t tcp_server_client_connection::write(void const * buf, size_t count)
135 : {
136 0 : if(!f_client)
137 : {
138 0 : errno = EBADF;
139 0 : return -1;
140 : }
141 0 : return f_client->write(reinterpret_cast<char const *>(buf), count);
142 : }
143 :
144 :
145 : /** \brief Close the socket of this connection.
146 : *
147 : * This function is automatically called whenever the object gets
148 : * destroyed (see destructor) or detects that the client closed
149 : * the network connection.
150 : *
151 : * Connections cannot be reopened.
152 : */
153 0 : void tcp_server_client_connection::close()
154 : {
155 0 : f_client.reset();
156 0 : }
157 :
158 :
159 : /** \brief Retrieve the socket of this connection.
160 : *
161 : * This function returns the socket defined in this connection.
162 : */
163 0 : int tcp_server_client_connection::get_socket() const
164 : {
165 0 : if(f_client == nullptr)
166 : {
167 : // client connection was closed
168 : //
169 0 : return -1;
170 : }
171 0 : return f_client->get_socket();
172 : }
173 :
174 :
175 : /** \brief Tell that we are always a reader.
176 : *
177 : * This function always returns true meaning that the connection is
178 : * always of a reader. In most cases this is safe because if nothing
179 : * is being written to you then poll() never returns so you do not
180 : * waste much time in have a TCP connection always marked as a
181 : * reader.
182 : *
183 : * \return The events to listen to for this connection.
184 : */
185 0 : bool tcp_server_client_connection::is_reader() const
186 : {
187 0 : return true;
188 : }
189 :
190 :
191 : /** \brief Retrieve a copy of the client's address.
192 : *
193 : * This function makes a copy of the address of this client connection
194 : * to the \p address parameter and returns the length.
195 : *
196 : * If the function returns zero, then the \p address buffer is not
197 : * modified and no address is defined in this connection.
198 : *
199 : * \param[out] address The reference to an address variable where the
200 : * client's address gets copied.
201 : *
202 : * \return Return the length of the address which may be smaller than
203 : * sizeof(address). If zero, then no address is defined.
204 : *
205 : * \sa get_addr()
206 : */
207 0 : size_t tcp_server_client_connection::get_client_address(sockaddr_storage & address) const
208 : {
209 : // make sure the address is defined and the socket open
210 : //
211 0 : if(const_cast<tcp_server_client_connection *>(this)->define_address() != 0)
212 : {
213 0 : return 0;
214 : }
215 :
216 0 : address = f_address;
217 0 : return f_length;
218 : }
219 :
220 :
221 : /** \brief Retrieve the address in the form of a string.
222 : *
223 : * Like the get_addr() of the tcp client and server classes, this
224 : * function returns the address in the form of a string which can
225 : * easily be used to log information and other similar tasks.
226 : *
227 : * \todo
228 : * Look at using libaddr for the convertion.
229 : *
230 : * \return The client's address in the form of a string.
231 : */
232 0 : std::string tcp_server_client_connection::get_client_addr() const
233 : {
234 : // make sure the address is defined and the socket open
235 : //
236 0 : if(!const_cast<tcp_server_client_connection *>(this)->define_address())
237 : {
238 0 : return std::string();
239 : }
240 :
241 0 : size_t const max_length(std::max(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) + 1);
242 :
243 : // in release mode this should not be dynamic (although the syntax is so
244 : // the warning would happen), but in debug it is likely an alloca()
245 : #pragma GCC diagnostic push
246 : #pragma GCC diagnostic ignored "-Wvla"
247 : char buf[max_length];
248 : #pragma GCC diagnostic pop
249 :
250 0 : char const * r(nullptr);
251 :
252 0 : if(f_address.ss_family == AF_INET)
253 : {
254 0 : r = inet_ntop(AF_INET, &reinterpret_cast<sockaddr_in const &>(f_address).sin_addr, buf, max_length);
255 : }
256 : else
257 : {
258 0 : r = inet_ntop(AF_INET6, &reinterpret_cast<sockaddr_in6 const &>(f_address).sin6_addr, buf, max_length);
259 : }
260 :
261 0 : if(r == nullptr)
262 : {
263 0 : int const e(errno);
264 0 : std::string err("inet_ntop() could not convert IP address (errno: ");
265 0 : err += std::to_string(e);
266 0 : err += " -- ";
267 0 : err += strerror(e);
268 0 : err += ").";
269 0 : SNAP_LOG_FATAL << err;
270 0 : throw event_dispatcher_runtime_error(err);
271 : }
272 :
273 0 : return buf;
274 : }
275 :
276 :
277 : /** \brief Retrieve the port.
278 : *
279 : * This function returns the port of the socket on our side.
280 : *
281 : * If the port is not available (not connected?), then -1 is returned.
282 : *
283 : * \return The client's port in host order.
284 : */
285 0 : int tcp_server_client_connection::get_client_port() const
286 : {
287 : // make sure the address is defined and the socket open
288 : //
289 0 : if(!const_cast<tcp_server_client_connection *>(this)->define_address())
290 : {
291 0 : return -1;
292 : }
293 :
294 0 : if(f_address.ss_family == AF_INET)
295 : {
296 0 : return ntohs(reinterpret_cast<sockaddr_in const &>(f_address).sin_port);
297 : }
298 : else
299 : {
300 0 : return ntohs(reinterpret_cast<sockaddr_in6 const &>(f_address).sin6_port);
301 : }
302 : }
303 :
304 :
305 : /** \brief Retrieve the address in the form of a string.
306 : *
307 : * Like the get_addr() of the tcp client and server classes, this
308 : * function returns the address in the form of a string which can
309 : * easily be used to log information and other similar tasks.
310 : *
311 : * \todo
312 : * Look at using libaddr for the convertion.
313 : *
314 : * \return The client's address in the form of a string.
315 : */
316 0 : std::string tcp_server_client_connection::get_client_addr_port() const
317 : {
318 : // get the current address and port
319 0 : std::string const addr(get_client_addr());
320 0 : int const port(get_client_port());
321 :
322 : // make sure they are defined
323 0 : if(addr.empty()
324 0 : || port < 0)
325 : {
326 0 : return std::string();
327 : }
328 :
329 : // calculate the result
330 0 : std::string buf;
331 0 : buf.reserve(addr.length() + (3 + 5));
332 0 : if(f_address.ss_family == AF_INET)
333 : {
334 0 : buf += addr;
335 0 : buf += ':';
336 : }
337 : else
338 : {
339 0 : buf += '[';
340 0 : buf += addr;
341 0 : buf += "]:";
342 : }
343 0 : buf += std::to_string(port);
344 :
345 0 : return buf;
346 : }
347 :
348 :
349 : /** \brief Retrieve the socket address if we have not done so yet.
350 : *
351 : * This function make sure that the f_address and f_length parameters are
352 : * defined. This is done by calling the getsockname() function.
353 : *
354 : * If f_length is still zero, then it is expected that address was not
355 : * yet read.
356 : *
357 : * Note that the function returns -1 if the socket is now -1 (i.e. the
358 : * connection is closed) whether or not the function worked before.
359 : *
360 : * \return false if the address cannot be defined, true otherwise
361 : */
362 0 : bool tcp_server_client_connection::define_address()
363 : {
364 0 : int const s(get_socket());
365 0 : if(s == -1)
366 : {
367 0 : return false;
368 : }
369 :
370 0 : if(f_length == 0)
371 : {
372 : // address not defined yet, retrieve with with getsockname()
373 : //
374 0 : f_length = sizeof(f_address);
375 0 : if(getsockname(s, reinterpret_cast<struct sockaddr *>(&f_address), &f_length) != 0)
376 : {
377 0 : int const e(errno);
378 : SNAP_LOG_ERROR
379 0 : << "getsockname() failed retrieving IP address (errno: "
380 0 : << e
381 0 : << " -- "
382 0 : << strerror(e)
383 0 : << ").";
384 0 : f_length = 0;
385 0 : return false;
386 : }
387 0 : if(f_address.ss_family != AF_INET
388 0 : && f_address.ss_family != AF_INET6)
389 : {
390 : SNAP_LOG_ERROR
391 0 : << "address family ("
392 0 : << f_address.ss_family
393 0 : << ") returned by getsockname() is not understood, it is neither an IPv4 nor IPv6.";
394 0 : f_length = 0;
395 0 : return false;
396 : }
397 0 : if(f_length < sizeof(f_address))
398 : {
399 : // reset the rest of the structure, just in case
400 : //
401 0 : memset(reinterpret_cast<char *>(&f_address) + f_length, 0, sizeof(f_address) - f_length);
402 : }
403 : }
404 :
405 0 : return true;
406 : }
407 :
408 :
409 :
410 : } // namespace ed
411 : // vim: ts=4 sw=4 et
|