Line data Source code
1 : // Copyright (c) 2012-2021 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 along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 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_message_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 <cstring>
57 :
58 :
59 : // C lib
60 : //
61 : #include <arpa/inet.h>
62 : #include <sys/socket.h>
63 :
64 :
65 : // last include
66 : //
67 : #include <snapdev/poison.h>
68 :
69 :
70 :
71 : namespace ed
72 : {
73 :
74 :
75 :
76 : /** \brief Initializes a client to read messages from a socket.
77 : *
78 : * This implementation creates a message in/out client.
79 : * This is the most useful client in our Snap! Communicator
80 : * as it directly sends and receives messages.
81 : *
82 : * \todo
83 : * Convert the socket address to string using libaddr.
84 : *
85 : * \param[in] client The client representing the in/out socket.
86 : */
87 0 : tcp_server_client_message_connection::tcp_server_client_message_connection(tcp_bio_client::pointer_t client)
88 0 : : tcp_server_client_buffer_connection(client)
89 : {
90 : // TODO: somehow the port seems wrong (i.e. all connections return the same port)
91 :
92 : // make sure the socket is defined and well
93 : //
94 0 : int const socket(client->get_socket());
95 0 : if(socket < 0)
96 : {
97 0 : SNAP_LOG_ERROR
98 0 : << "called with a closed client connection."
99 : << SNAP_LOG_SEND;
100 0 : throw std::runtime_error("tcp_server_client_message_connection() called with a closed client connection.");
101 : }
102 :
103 0 : struct sockaddr_storage address = sockaddr_storage();
104 0 : socklen_t length(sizeof(address));
105 0 : if(getpeername(socket, reinterpret_cast<struct sockaddr *>(&address), &length) != 0)
106 : {
107 0 : int const e(errno);
108 0 : SNAP_LOG_ERROR
109 0 : << "getpeername() failed retrieving IP address (errno: "
110 : << e
111 : << " -- "
112 0 : << strerror(e)
113 : << ")."
114 : << SNAP_LOG_SEND;
115 0 : throw std::runtime_error("getpeername() failed to retrieve IP address in tcp_server_client_message_connection()");
116 : }
117 0 : if(address.ss_family != AF_INET
118 0 : && address.ss_family != AF_INET6)
119 : {
120 0 : SNAP_LOG_ERROR
121 0 : << "address family ("
122 0 : << address.ss_family
123 : << ") returned by getpeername() is not understood, it is neither an IPv4 nor IPv6."
124 : << SNAP_LOG_SEND;
125 0 : throw std::runtime_error("getpeername() returned an address which is not understood in tcp_server_client_message_connection()");
126 : }
127 0 : if(length < sizeof(address))
128 : {
129 : // reset the rest of the structure, just in case
130 0 : memset(reinterpret_cast<char *>(&address) + length, 0, sizeof(address) - length);
131 : }
132 :
133 0 : constexpr size_t max_length(std::max(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) + 1);
134 :
135 : // in release mode this should not be dynamic (although the syntax is so
136 : // the warning would happen), but in debug it is likely an alloca()
137 : //
138 : #pragma GCC diagnostic push
139 : #pragma GCC diagnostic ignored "-Wvla"
140 0 : char buf[max_length];
141 : #pragma GCC diagnostic pop
142 :
143 0 : char const * r(nullptr);
144 :
145 0 : if(address.ss_family == AF_INET)
146 : {
147 0 : r = inet_ntop(AF_INET, &reinterpret_cast<struct sockaddr_in const &>(address).sin_addr, buf, max_length);
148 : }
149 : else
150 : {
151 0 : r = inet_ntop(AF_INET6, &reinterpret_cast<struct sockaddr_in6 const &>(address).sin6_addr, buf, max_length);
152 : }
153 :
154 0 : if(r == nullptr)
155 : {
156 0 : int const e(errno);
157 0 : std::string err("inet_ntop() could not convert IP address (errno: ");
158 0 : err += std::to_string(e);
159 0 : err += " -- ";
160 0 : err += strerror(e);
161 0 : err += ").";
162 0 : SNAP_LOG_FATAL << err << SNAP_LOG_SEND;
163 0 : throw event_dispatcher_runtime_error(err);
164 : }
165 :
166 0 : if(address.ss_family == AF_INET)
167 : {
168 0 : f_remote_address = buf;
169 0 : f_remote_address += ':';
170 0 : f_remote_address += std::to_string(static_cast<int>(ntohs(reinterpret_cast<sockaddr_in const &>(address).sin_port)));
171 : }
172 : else
173 : {
174 0 : f_remote_address = "[";
175 0 : f_remote_address += buf;
176 0 : f_remote_address += "]:";
177 0 : f_remote_address += std::to_string(static_cast<int>(ntohs(reinterpret_cast<sockaddr_in6 const &>(address).sin6_port)));
178 : }
179 0 : }
180 :
181 :
182 : /** \brief Process a line (string) just received.
183 : *
184 : * The function parses the line as a message and then calls the
185 : * process_message() function if the line was valid.
186 : *
187 : * \param[in] line The line of text that was just read.
188 : */
189 0 : void tcp_server_client_message_connection::process_line(std::string const & line)
190 : {
191 : // empty lines should not occur, but just in case, just ignore
192 0 : if(line.empty())
193 : {
194 0 : return;
195 : }
196 :
197 0 : message msg;
198 0 : if(msg.from_message(line))
199 : {
200 0 : dispatch_message(msg);
201 : }
202 : else
203 : {
204 : // TODO: what to do here? This could because the version changed
205 : // and the messages are not compatible anymore.
206 : //
207 0 : SNAP_LOG_ERROR
208 0 : << "process_line() was asked to process an invalid message ("
209 : << line
210 : << ")"
211 : << SNAP_LOG_SEND;
212 : }
213 : }
214 :
215 :
216 : /** \brief Send a message.
217 : *
218 : * This function sends a message to the client on the other side
219 : * of this connection.
220 : *
221 : * \exception event_dispatcher_runtime_error
222 : * This function throws this exception if the write() to the pipe
223 : * fails to write the entire message. This should only happen if
224 : * the pipe gets severed.
225 : *
226 : * \param[in] message The message to be processed.
227 : * \param[in] cache Whether to cache the message if there is no connection.
228 : * (Ignore because a client socket has to be there until
229 : * closed and then it can't be reopened by the server.)
230 : *
231 : * \return Always true, although if an error occurs the function throws.
232 : */
233 0 : bool tcp_server_client_message_connection::send_message(message const & msg, bool cache)
234 : {
235 0 : snap::NOT_USED(cache);
236 :
237 : // transform the message to a string and write to the socket
238 : // the writing is asynchronous so the message is saved in a cache
239 : // and transferred only later when the run() loop is hit again
240 : //
241 0 : std::string buf(msg.to_message());
242 0 : buf += '\n';
243 0 : return write(buf.c_str(), buf.length()) == static_cast<ssize_t>(buf.length());
244 : }
245 :
246 :
247 : /** \brief Retrieve the remote address information.
248 : *
249 : * This function can be used to retrieve the remove address and port
250 : * information as was specified on the constructor. These can be used
251 : * to find this specific connection at a later time or create another
252 : * connection.
253 : *
254 : * For example, you may get 192.168.2.17:4040.
255 : *
256 : * The function works even after the socket gets closed as we save
257 : * the remote address and port in a string just after the connection
258 : * was established.
259 : *
260 : * \warning
261 : * This function returns BOTH: the address and the port.
262 : *
263 : * \note
264 : * These parameters are the same as what was passed to the constructor,
265 : * only both will have been converted to numbers. So for example when
266 : * you used "localhost", here you get "::1" or "127.0.0.1" for the
267 : * address.
268 : *
269 : * \return The remote host address and connection port.
270 : */
271 0 : std::string const & tcp_server_client_message_connection::get_remote_address() const
272 : {
273 0 : return f_remote_address;
274 : }
275 :
276 :
277 6 : } // namespace ed
278 : // vim: ts=4 sw=4 et
|