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