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