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 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 Contruct 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_socket(-1)
88 : , f_port(port)
89 0 : , f_addr(addr)
90 : {
91 0 : if(f_port < 0 || f_port >= 65536)
92 : {
93 0 : throw event_dispatcher_invalid_parameter("invalid port for a client socket");
94 : }
95 0 : if(f_addr.empty())
96 : {
97 0 : throw event_dispatcher_invalid_parameter("an empty address is not valid for a client socket");
98 : }
99 :
100 : addrinfo hints;
101 0 : memset(&hints, 0, sizeof(hints));
102 0 : hints.ai_family = AF_UNSPEC;
103 0 : hints.ai_socktype = SOCK_STREAM;
104 0 : hints.ai_protocol = IPPROTO_TCP;
105 0 : std::string const port_str(std::to_string(f_port));
106 0 : addrinfo * addrinfo(nullptr);
107 0 : int const r(getaddrinfo(addr.c_str(), port_str.c_str(), &hints, &addrinfo));
108 0 : raii_addrinfo_t addr_info(addrinfo);
109 0 : if(r != 0
110 0 : || addrinfo == nullptr)
111 : {
112 0 : int const e(errno);
113 0 : std::string err("getaddrinfo() failed to parse the address or port \"");
114 0 : err += addr;
115 0 : err += ":";
116 0 : err += port_str;
117 0 : err += "\" strings (errno: ";
118 0 : err += std::to_string(e);
119 0 : err += " -- ";
120 0 : err += strerror(e);
121 0 : err += ")";
122 0 : SNAP_LOG_FATAL << err;
123 0 : throw event_dispatcher_runtime_error(err);
124 : }
125 :
126 0 : f_socket = socket(addr_info.get()->ai_family, SOCK_STREAM, IPPROTO_TCP);
127 0 : if(f_socket < 0)
128 : {
129 0 : int const e(errno);
130 : SNAP_LOG_FATAL
131 0 : << "socket() failed to create a socket descriptor (errno: "
132 0 : << e
133 0 : << " -- "
134 0 : << strerror(e)
135 0 : << ")";
136 0 : throw event_dispatcher_runtime_error("could not create socket for client");
137 : }
138 :
139 0 : if(connect(f_socket, addr_info.get()->ai_addr, addr_info.get()->ai_addrlen) < 0)
140 : {
141 0 : int const e(errno);
142 : SNAP_LOG_FATAL
143 0 : << "connect() failed to connect a socket (errno: "
144 0 : << e
145 0 : << " -- "
146 0 : << strerror(e)
147 0 : << ")";
148 0 : close(f_socket);
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 : close(f_socket);
164 0 : }
165 :
166 : /** \brief Get the socket descriptor.
167 : *
168 : * This function returns the TCP client socket descriptor. This can be
169 : * used to change the descriptor behavior (i.e. make it non-blocking for
170 : * example.)
171 : *
172 : * \return The socket descriptor.
173 : */
174 0 : int tcp_client::get_socket() const
175 : {
176 0 : return f_socket;
177 : }
178 :
179 : /** \brief Get the TCP client port.
180 : *
181 : * This function returns the port used when creating the TCP client.
182 : * Note that this is the port the server is listening to and not the port
183 : * the TCP client is currently connected to.
184 : *
185 : * \return The TCP client port.
186 : */
187 0 : int tcp_client::get_port() const
188 : {
189 0 : return f_port;
190 : }
191 :
192 : /** \brief Get the TCP server address.
193 : *
194 : * This function returns the address used when creating the TCP address as is.
195 : * Note that this is the address of the server where the client is connected
196 : * and not the address where the client is running (although it may be the
197 : * same.)
198 : *
199 : * Use the get_client_addr() function to retrieve the client's TCP address.
200 : *
201 : * \return The TCP client address.
202 : */
203 0 : std::string tcp_client::get_addr() const
204 : {
205 0 : return f_addr;
206 : }
207 :
208 : /** \brief Get the TCP client port.
209 : *
210 : * This function retrieve the port of the client (used on your computer).
211 : * This is retrieved from the socket using the getsockname() function.
212 : *
213 : * \return The port or -1 if it cannot be determined.
214 : */
215 0 : int tcp_client::get_client_port() const
216 : {
217 : struct sockaddr addr;
218 0 : socklen_t len(sizeof(addr));
219 0 : int r(getsockname(f_socket, &addr, &len));
220 0 : if(r != 0)
221 : {
222 0 : return -1;
223 : }
224 : // Note: I know the port is at the exact same location in both
225 : // structures in Linux but it could change on other Unices
226 0 : if(addr.sa_family == AF_INET)
227 : {
228 : // IPv4
229 0 : return reinterpret_cast<sockaddr_in *>(&addr)->sin_port;
230 : }
231 0 : if(addr.sa_family == AF_INET6)
232 : {
233 : // IPv6
234 0 : return reinterpret_cast<sockaddr_in6 *>(&addr)->sin6_port;
235 : }
236 0 : return -1;
237 : }
238 :
239 : /** \brief Get the TCP client address.
240 : *
241 : * This function retrieve the IP address of the client (your computer).
242 : * This is retrieved from the socket using the getsockname() function.
243 : *
244 : * \return The IP address as a string.
245 : */
246 0 : std::string tcp_client::get_client_addr() const
247 : {
248 : struct sockaddr addr;
249 0 : socklen_t len(sizeof(addr));
250 0 : int const r(getsockname(f_socket, &addr, &len));
251 0 : if(r != 0)
252 : {
253 0 : throw event_dispatcher_runtime_error("address not available");
254 : }
255 : char buf[BUFSIZ];
256 0 : switch(addr.sa_family)
257 : {
258 : case AF_INET:
259 0 : if(len < sizeof(struct sockaddr_in))
260 : {
261 0 : throw event_dispatcher_runtime_error("address size incompatible (AF_INET)");
262 : }
263 0 : inet_ntop(AF_INET, &reinterpret_cast<struct sockaddr_in *>(&addr)->sin_addr, buf, sizeof(buf));
264 0 : break;
265 :
266 : case AF_INET6:
267 0 : if(len < sizeof(struct sockaddr_in6))
268 : {
269 0 : throw event_dispatcher_runtime_error("address size incompatible (AF_INET6)");
270 : }
271 0 : inet_ntop(AF_INET6, &reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_addr, buf, sizeof(buf));
272 0 : break;
273 :
274 : default:
275 0 : throw event_dispatcher_runtime_error("unknown address family");
276 :
277 : }
278 0 : return buf;
279 : }
280 :
281 : /** \brief Read data from the socket.
282 : *
283 : * A TCP socket is a stream type of socket and one can read data from it
284 : * as if it were a regular file. This function reads \p size bytes and
285 : * returns. The function returns early if the server closes the connection.
286 : *
287 : * If your socket is blocking, \p size should be exactly what you are
288 : * expecting or this function will block forever or until the server
289 : * closes the connection.
290 : *
291 : * The function returns -1 if an error occurs. The error is available in
292 : * errno as expected in the POSIX interface.
293 : *
294 : * \param[in,out] buf The buffer where the data is read.
295 : * \param[in] size The size of the buffer.
296 : *
297 : * \return The number of bytes read from the socket, or -1 on errors.
298 : */
299 0 : int tcp_client::read(char *buf, size_t size)
300 : {
301 0 : return static_cast<int>(::read(f_socket, buf, size));
302 : }
303 :
304 :
305 : /** \brief Read one line.
306 : *
307 : * This function reads one line from the current location up to the next
308 : * '\\n' character. We do not have any special handling of the '\\r'
309 : * character.
310 : *
311 : * The function may return 0 in which case the server closed the connection.
312 : *
313 : * \param[out] line The resulting line read from the server.
314 : *
315 : * \return The number of bytes read from the socket, or -1 on errors.
316 : * If the function returns 0 or more, then the \p line parameter
317 : * represents the characters read on the network.
318 : */
319 0 : int tcp_client::read_line(std::string& line)
320 : {
321 0 : line.clear();
322 0 : int len(0);
323 0 : for(;;)
324 : {
325 : char c;
326 0 : int r(read(&c, sizeof(c)));
327 0 : if(r <= 0)
328 : {
329 0 : return len == 0 && r < 0 ? -1 : len;
330 : }
331 0 : if(c == '\n')
332 : {
333 0 : return len;
334 : }
335 0 : ++len;
336 0 : line += c;
337 : }
338 : }
339 :
340 :
341 : /** \brief Write data to the socket.
342 : *
343 : * A TCP socket is a stream type of socket and one can write data to it
344 : * as if it were a regular file. This function writes \p size bytes to
345 : * the socket and then returns. This function returns early if the server
346 : * closes the connection.
347 : *
348 : * If your socket is not blocking, less than \p size bytes may be written
349 : * to the socket. In that case you are responsible for calling the function
350 : * again to write the remainder of the buffer until the function returns
351 : * a number of bytes written equal to \p size.
352 : *
353 : * The function returns -1 if an error occurs. The error is available in
354 : * errno as expected in the POSIX interface.
355 : *
356 : * \param[in] buf The buffer with the data to send over the socket.
357 : * \param[in] size The number of bytes in buffer to send over the socket.
358 : *
359 : * \return The number of bytes that were actually accepted by the socket
360 : * or -1 if an error occurs.
361 : */
362 0 : int tcp_client::write(const char *buf, size_t size)
363 : {
364 0 : return static_cast<int>(::write(f_socket, buf, size));
365 : }
366 :
367 :
368 :
369 6 : } // namespace tcp_client_server
370 : // vim: ts=4 sw=4 et
|