Line data Source code
1 : // Event Dispatcher
2 : // Copyright (c) 2012-2019 Made to Order Software Corp. All Rights Reserved
3 : //
4 : // This program is free software; you can redistribute it and/or modify
5 : // it under the terms of the GNU General Public License as published by
6 : // the Free Software Foundation; either version 2 of the License, or
7 : // (at your option) any later version.
8 : //
9 : // This program is distributed in the hope that it will be useful,
10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : // GNU General Public License for more details.
13 : //
14 : // You should have received a copy of the GNU General Public License
15 : // along with this program; if not, write to the Free Software
16 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 :
18 :
19 : // make sure we use OpenSSL with multi-thread support
20 : // (TODO: move to .cpp once we have the impl!)
21 : #define OPENSSL_THREAD_DEFINES
22 :
23 :
24 : // self
25 : //
26 : #include "eventdispatcher/tcp_client.h"
27 :
28 : #include "eventdispatcher/exception.h"
29 : #include "eventdispatcher/utils.h"
30 :
31 :
32 : // addr lib
33 : //
34 : //#include "libaddr/addr.h"
35 :
36 :
37 : // snaplogger lib
38 : //
39 : #include "snaplogger/message.h"
40 :
41 :
42 : //// snapdev lib
43 : ////
44 : //#include "snapdev/not_reached.h"
45 : //#include "snapdev/not_used.h"
46 : //#include "snapdev/raii_generic_deleter.h"
47 : //
48 : //
49 : //// C++
50 : ////
51 : //#include <sstream>
52 : //#include <iomanip>
53 : //
54 : //
55 : //// C lib
56 : ////
57 : #include <netdb.h>
58 : #include <arpa/inet.h>
59 : //#include <poll.h>
60 : #include <string.h>
61 : //#include <sys/ioctl.h>
62 : //#include <sys/socket.h>
63 : //#include <sys/types.h>
64 : //#include <unistd.h>
65 :
66 :
67 : // last include
68 : //
69 : #include "snapdev/poison.h"
70 :
71 :
72 :
73 :
74 : //#ifndef OPENSSL_THREADS
75 : //#error "OPENSSL_THREADS is not defined. Snap! requires support for multiple threads in OpenSSL."
76 : //#endif
77 :
78 : namespace ed
79 : {
80 :
81 :
82 :
83 : /** \class tcp_client
84 : * \brief Create a client socket and connect to a server.
85 : *
86 : * This class is a client socket implementation used to connect to a server.
87 : * The server is expected to be running at the time the client is created
88 : * otherwise it fails connecting.
89 : *
90 : * This class is not appropriate to connect to a server that may come and go
91 : * over time.
92 : */
93 :
94 : /** \brief Contruct a tcp_client object.
95 : *
96 : * The tcp_client constructor initializes a TCP client object by connecting
97 : * to the specified server. The server is defined with the \p addr and
98 : * \p port specified as parameters.
99 : *
100 : * \exception tcp_client_server_parameter_error
101 : * This exception is raised if the \p port parameter is out of range or the
102 : * IP address is an empty string or otherwise an invalid address.
103 : *
104 : * \exception tcp_client_server_runtime_error
105 : * This exception is raised if the client cannot create the socket or it
106 : * cannot connect to the server.
107 : *
108 : * \param[in] addr The address of the server to connect to. It must be valid.
109 : * \param[in] port The port the server is listening on.
110 : */
111 0 : tcp_client::tcp_client(std::string const & addr, int port)
112 : : f_socket(-1)
113 : , f_port(port)
114 0 : , f_addr(addr)
115 : {
116 0 : if(f_port < 0 || f_port >= 65536)
117 : {
118 0 : throw event_dispatcher_invalid_parameter("invalid port for a client socket");
119 : }
120 0 : if(f_addr.empty())
121 : {
122 0 : throw event_dispatcher_invalid_parameter("an empty address is not valid for a client socket");
123 : }
124 :
125 : addrinfo hints;
126 0 : memset(&hints, 0, sizeof(hints));
127 0 : hints.ai_family = AF_UNSPEC;
128 0 : hints.ai_socktype = SOCK_STREAM;
129 0 : hints.ai_protocol = IPPROTO_TCP;
130 0 : std::string const port_str(std::to_string(f_port));
131 0 : addrinfo * addrinfo(nullptr);
132 0 : int const r(getaddrinfo(addr.c_str(), port_str.c_str(), &hints, &addrinfo));
133 0 : raii_addrinfo_t addr_info(addrinfo);
134 0 : if(r != 0
135 0 : || addrinfo == nullptr)
136 : {
137 0 : int const e(errno);
138 0 : std::string err("getaddrinfo() failed to parse the address or port \"");
139 0 : err += addr;
140 0 : err += ":";
141 0 : err += port_str;
142 0 : err += "\" strings (errno: ";
143 0 : err += std::to_string(e);
144 0 : err += " -- ";
145 0 : err += strerror(e);
146 0 : err += ")";
147 0 : SNAP_LOG_FATAL << err;
148 0 : throw event_dispatcher_runtime_error(err);
149 : }
150 :
151 0 : f_socket = socket(addr_info.get()->ai_family, SOCK_STREAM, IPPROTO_TCP);
152 0 : if(f_socket < 0)
153 : {
154 0 : int const e(errno);
155 : SNAP_LOG_FATAL
156 0 : << "socket() failed to create a socket descriptor (errno: "
157 0 : << e
158 0 : << " -- "
159 0 : << strerror(e)
160 0 : << ")";
161 0 : throw event_dispatcher_runtime_error("could not create socket for client");
162 : }
163 :
164 0 : if(connect(f_socket, addr_info.get()->ai_addr, addr_info.get()->ai_addrlen) < 0)
165 : {
166 0 : int const e(errno);
167 : SNAP_LOG_FATAL
168 0 : << "connect() failed to connect a socket (errno: "
169 0 : << e
170 0 : << " -- "
171 0 : << strerror(e)
172 0 : << ")";
173 0 : close(f_socket);
174 0 : throw event_dispatcher_runtime_error("could not connect client socket to \"" + f_addr + "\"");
175 : }
176 0 : }
177 :
178 : /** \brief Clean up the TCP client object.
179 : *
180 : * This function cleans up the TCP client object by closing the attached socket.
181 : *
182 : * \note
183 : * DO NOT use the shutdown() call since we may end up forking and using
184 : * that connection in the child.
185 : */
186 0 : tcp_client::~tcp_client()
187 : {
188 0 : close(f_socket);
189 0 : }
190 :
191 : /** \brief Get the socket descriptor.
192 : *
193 : * This function returns the TCP client socket descriptor. This can be
194 : * used to change the descriptor behavior (i.e. make it non-blocking for
195 : * example.)
196 : *
197 : * \return The socket descriptor.
198 : */
199 0 : int tcp_client::get_socket() const
200 : {
201 0 : return f_socket;
202 : }
203 :
204 : /** \brief Get the TCP client port.
205 : *
206 : * This function returns the port used when creating the TCP client.
207 : * Note that this is the port the server is listening to and not the port
208 : * the TCP client is currently connected to.
209 : *
210 : * \return The TCP client port.
211 : */
212 0 : int tcp_client::get_port() const
213 : {
214 0 : return f_port;
215 : }
216 :
217 : /** \brief Get the TCP server address.
218 : *
219 : * This function returns the address used when creating the TCP address as is.
220 : * Note that this is the address of the server where the client is connected
221 : * and not the address where the client is running (although it may be the
222 : * same.)
223 : *
224 : * Use the get_client_addr() function to retrieve the client's TCP address.
225 : *
226 : * \return The TCP client address.
227 : */
228 0 : std::string tcp_client::get_addr() const
229 : {
230 0 : return f_addr;
231 : }
232 :
233 : /** \brief Get the TCP client port.
234 : *
235 : * This function retrieve the port of the client (used on your computer).
236 : * This is retrieved from the socket using the getsockname() function.
237 : *
238 : * \return The port or -1 if it cannot be determined.
239 : */
240 0 : int tcp_client::get_client_port() const
241 : {
242 : struct sockaddr addr;
243 0 : socklen_t len(sizeof(addr));
244 0 : int r(getsockname(f_socket, &addr, &len));
245 0 : if(r != 0)
246 : {
247 0 : return -1;
248 : }
249 : // Note: I know the port is at the exact same location in both
250 : // structures in Linux but it could change on other Unices
251 0 : if(addr.sa_family == AF_INET)
252 : {
253 : // IPv4
254 0 : return reinterpret_cast<sockaddr_in *>(&addr)->sin_port;
255 : }
256 0 : if(addr.sa_family == AF_INET6)
257 : {
258 : // IPv6
259 0 : return reinterpret_cast<sockaddr_in6 *>(&addr)->sin6_port;
260 : }
261 0 : return -1;
262 : }
263 :
264 : /** \brief Get the TCP client address.
265 : *
266 : * This function retrieve the IP address of the client (your computer).
267 : * This is retrieved from the socket using the getsockname() function.
268 : *
269 : * \return The IP address as a string.
270 : */
271 0 : std::string tcp_client::get_client_addr() const
272 : {
273 : struct sockaddr addr;
274 0 : socklen_t len(sizeof(addr));
275 0 : int const r(getsockname(f_socket, &addr, &len));
276 0 : if(r != 0)
277 : {
278 0 : throw event_dispatcher_runtime_error("address not available");
279 : }
280 : char buf[BUFSIZ];
281 0 : switch(addr.sa_family)
282 : {
283 : case AF_INET:
284 0 : if(len < sizeof(struct sockaddr_in))
285 : {
286 0 : throw event_dispatcher_runtime_error("address size incompatible (AF_INET)");
287 : }
288 0 : inet_ntop(AF_INET, &reinterpret_cast<struct sockaddr_in *>(&addr)->sin_addr, buf, sizeof(buf));
289 0 : break;
290 :
291 : case AF_INET6:
292 0 : if(len < sizeof(struct sockaddr_in6))
293 : {
294 0 : throw event_dispatcher_runtime_error("address size incompatible (AF_INET6)");
295 : }
296 0 : inet_ntop(AF_INET6, &reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_addr, buf, sizeof(buf));
297 0 : break;
298 :
299 : default:
300 0 : throw event_dispatcher_runtime_error("unknown address family");
301 :
302 : }
303 0 : return buf;
304 : }
305 :
306 : /** \brief Read data from the socket.
307 : *
308 : * A TCP socket is a stream type of socket and one can read data from it
309 : * as if it were a regular file. This function reads \p size bytes and
310 : * returns. The function returns early if the server closes the connection.
311 : *
312 : * If your socket is blocking, \p size should be exactly what you are
313 : * expecting or this function will block forever or until the server
314 : * closes the connection.
315 : *
316 : * The function returns -1 if an error occurs. The error is available in
317 : * errno as expected in the POSIX interface.
318 : *
319 : * \param[in,out] buf The buffer where the data is read.
320 : * \param[in] size The size of the buffer.
321 : *
322 : * \return The number of bytes read from the socket, or -1 on errors.
323 : */
324 0 : int tcp_client::read(char *buf, size_t size)
325 : {
326 0 : return static_cast<int>(::read(f_socket, buf, size));
327 : }
328 :
329 :
330 : /** \brief Read one line.
331 : *
332 : * This function reads one line from the current location up to the next
333 : * '\\n' character. We do not have any special handling of the '\\r'
334 : * character.
335 : *
336 : * The function may return 0 in which case the server closed the connection.
337 : *
338 : * \param[out] line The resulting line read from the server.
339 : *
340 : * \return The number of bytes read from the socket, or -1 on errors.
341 : * If the function returns 0 or more, then the \p line parameter
342 : * represents the characters read on the network.
343 : */
344 0 : int tcp_client::read_line(std::string& line)
345 : {
346 0 : line.clear();
347 0 : int len(0);
348 0 : for(;;)
349 : {
350 : char c;
351 0 : int r(read(&c, sizeof(c)));
352 0 : if(r <= 0)
353 : {
354 0 : return len == 0 && r < 0 ? -1 : len;
355 : }
356 0 : if(c == '\n')
357 : {
358 0 : return len;
359 : }
360 0 : ++len;
361 0 : line += c;
362 : }
363 : }
364 :
365 :
366 : /** \brief Write data to the socket.
367 : *
368 : * A TCP socket is a stream type of socket and one can write data to it
369 : * as if it were a regular file. This function writes \p size bytes to
370 : * the socket and then returns. This function returns early if the server
371 : * closes the connection.
372 : *
373 : * If your socket is not blocking, less than \p size bytes may be written
374 : * to the socket. In that case you are responsible for calling the function
375 : * again to write the remainder of the buffer until the function returns
376 : * a number of bytes written equal to \p size.
377 : *
378 : * The function returns -1 if an error occurs. The error is available in
379 : * errno as expected in the POSIX interface.
380 : *
381 : * \param[in] buf The buffer with the data to send over the socket.
382 : * \param[in] size The number of bytes in buffer to send over the socket.
383 : *
384 : * \return The number of bytes that were actually accepted by the socket
385 : * or -1 if an error occurs.
386 : */
387 0 : int tcp_client::write(const char *buf, size_t size)
388 : {
389 0 : return static_cast<int>(::write(f_socket, buf, size));
390 : }
391 :
392 :
393 :
394 6 : } // namespace tcp_client_server
395 : // vim: ts=4 sw=4 et
|