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