Line data Source code
1 : // Copyright (c) 2012-2022 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 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
36 : //
37 : #include <snaplogger/message.h>
38 :
39 :
40 : // C
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 by the \p address object.
73 : *
74 : * \exception invalid_parameter
75 : * This exception is raised if the \p port parameter is out of range or the
76 : * IP address is an empty string or otherwise an invalid address.
77 : *
78 : * \exception runtime_error
79 : * This exception is raised if the client cannot create the socket or it
80 : * cannot connect to the server.
81 : *
82 : * \param[in] address The address of the server to connect to. It must be valid.
83 : */
84 0 : tcp_client::tcp_client(addr::addr const & address)
85 0 : : f_address(address)
86 : {
87 0 : if(f_address.is_default())
88 : {
89 0 : throw invalid_parameter("the default address is not valid for a client socket");
90 : }
91 0 : if(f_address.get_protocol() != IPPROTO_TCP)
92 : {
93 0 : throw invalid_parameter("the address presents a protocol other than the expected TCP");
94 : }
95 :
96 0 : f_socket.reset(address.create_socket(0));
97 0 : if(f_socket < 0)
98 : {
99 0 : int const e(errno);
100 0 : SNAP_LOG_FATAL
101 0 : << "socket() failed to create a socket descriptor (errno: "
102 : << e
103 : << " -- "
104 0 : << strerror(e)
105 : << ")"
106 : << SNAP_LOG_SEND;
107 0 : throw runtime_error("could not create socket for client");
108 : }
109 :
110 0 : if(f_address.connect(f_socket.get()) != 0)
111 : {
112 0 : int const e(errno);
113 0 : std::stringstream ss;
114 0 : ss << "tcp_client() -- failed to connect() socket (errno: "
115 : << e
116 : << " -- "
117 0 : << strerror(e)
118 0 : << ")";
119 0 : SNAP_LOG_FATAL
120 : << ss
121 : << SNAP_LOG_SEND;
122 0 : throw runtime_error(ss.str());
123 : }
124 0 : }
125 :
126 : /** \brief Clean up the TCP client object.
127 : *
128 : * This function cleans up the TCP client object by closing the attached socket.
129 : *
130 : * \note
131 : * DO NOT use the shutdown() call since we may end up forking and using
132 : * that connection in the child.
133 : */
134 0 : tcp_client::~tcp_client()
135 : {
136 0 : }
137 :
138 : /** \brief Get the socket descriptor.
139 : *
140 : * This function returns the TCP client socket descriptor. This can be
141 : * used to change the descriptor behavior (i.e. make it non-blocking for
142 : * example.)
143 : *
144 : * \return The socket descriptor.
145 : */
146 0 : int tcp_client::get_socket() const
147 : {
148 0 : return f_socket.get();
149 : }
150 :
151 : /** \brief Get the TCP client port.
152 : *
153 : * This function returns the port used when creating the TCP client.
154 : * Note that this is the port the server is listening to and not the port
155 : * the TCP client is currently connected to.
156 : *
157 : * \return The TCP client port.
158 : */
159 0 : int tcp_client::get_port() const
160 : {
161 0 : return f_address.get_port();
162 : }
163 :
164 : /** \brief Get the TCP server address.
165 : *
166 : * This function returns the address used when creating the TCP address.
167 : * Note that this is the address of the server where the client is connected
168 : * and not the address where the client is running (although it may be the
169 : * same.)
170 : *
171 : * Use the get_client_addr() function to retrieve the client's TCP address.
172 : *
173 : * \return The TCP server address.
174 : */
175 0 : std::string tcp_client::get_addr() const
176 : {
177 0 : return f_address.to_ipv4or6_string(addr::addr::string_ip_t::STRING_IP_ONLY);
178 : }
179 :
180 : /** \brief Get the TCP server address.
181 : *
182 : * This function returns a copy of the address as specified in the contructor.
183 : *
184 : * \return The TCP server address/
185 : */
186 0 : addr::addr tcp_client::get_address() const
187 : {
188 0 : return f_address;
189 : }
190 :
191 : /** \brief Get the TCP client port.
192 : *
193 : * This function retrieve the port of the client (used on your computer).
194 : * This is retrieved from the socket using the getsockname() function.
195 : *
196 : * \return The port or -1 if it cannot be determined.
197 : *
198 : * \sa get_client_addr()
199 : * \sa get_client_address()
200 : */
201 0 : int tcp_client::get_client_port() const
202 : {
203 0 : struct sockaddr_in6 addr;
204 0 : struct sockaddr *a(reinterpret_cast<struct sockaddr *>(&addr));
205 0 : socklen_t len(sizeof(addr));
206 0 : int const r(getsockname(f_socket.get(), a, &len));
207 0 : if(r != 0)
208 : {
209 0 : return -1;
210 : }
211 :
212 : // Note: I know the port is at the exact same location in both
213 : // structures in Linux but it could change on other Unices
214 : //
215 0 : if(a->sa_family == AF_INET
216 0 : && len >= sizeof(sockaddr_in))
217 : {
218 : // IPv4
219 0 : return reinterpret_cast<sockaddr_in const *>(a)->sin_port;
220 : }
221 :
222 0 : if(a->sa_family == AF_INET6
223 0 : && len >= sizeof(sockaddr_in6))
224 : {
225 : // IPv6
226 0 : return addr.sin6_port;
227 : }
228 :
229 0 : return -1;
230 : }
231 :
232 : /** \brief Get the TCP client address.
233 : *
234 : * This function retrieve the IP address of the client (your computer).
235 : * This is retrieved from the socket using the getsockname() function.
236 : *
237 : * \exception runtime_error
238 : * The function raises this exception if the address we retrieve doesn't
239 : * match its type properly;
240 : *
241 : * \return The IP address as a string.
242 : *
243 : * \sa get_client_port()
244 : * \sa get_client_address()
245 : */
246 0 : std::string tcp_client::get_client_addr() const
247 : {
248 0 : sockaddr_in6 addr;
249 0 : sockaddr *a(reinterpret_cast<sockaddr *>(&addr));
250 0 : socklen_t len(sizeof(addr));
251 0 : int const r(getsockname(f_socket.get(), a, &len));
252 0 : if(r != 0)
253 : {
254 0 : throw runtime_error("address not available");
255 : }
256 :
257 0 : char buf[BUFSIZ];
258 0 : switch(a->sa_family)
259 : {
260 0 : case AF_INET:
261 0 : if(len < sizeof(sockaddr_in))
262 : {
263 0 : throw runtime_error("address size incompatible (AF_INET)");
264 : }
265 0 : inet_ntop(
266 : AF_INET
267 0 : , &reinterpret_cast<sockaddr_in *>(a)->sin_addr
268 : , buf
269 : , sizeof(buf));
270 0 : break;
271 :
272 0 : case AF_INET6:
273 0 : if(len < sizeof(sockaddr_in6))
274 : {
275 0 : throw runtime_error("address size incompatible (AF_INET6)");
276 : }
277 0 : inet_ntop(AF_INET6, &addr.sin6_addr, buf, sizeof(buf));
278 0 : break;
279 :
280 0 : default:
281 0 : throw runtime_error("unknown address family");
282 :
283 : }
284 :
285 0 : return buf;
286 : }
287 :
288 : /** \brief Get the TCP client address.
289 : *
290 : * This function retrieve the IP address of the client (your computer).
291 : * This is retrieved from the socket using the getsockname() function.
292 : *
293 : * \exception runtime_error
294 : * The function raises this exception if the address we retrieve doesn't
295 : * match its type properly;
296 : *
297 : * \return The IP address as a string.
298 : *
299 : * \sa get_client_port()
300 : * \sa get_client_address()
301 : */
302 0 : addr::addr tcp_client::get_client_address() const
303 : {
304 0 : addr::addr a;
305 0 : a.set_from_socket(f_socket.get(), true);
306 0 : return a;
307 : }
308 :
309 : /** \brief Read data from the socket.
310 : *
311 : * A TCP socket is a stream type of socket and one can read data from it
312 : * as if it were a regular file. This function reads \p size bytes and
313 : * returns. The function returns early if the server closes the connection.
314 : *
315 : * If your socket is blocking, \p size should be exactly what you are
316 : * expecting or this function will block forever or until the server
317 : * closes the connection.
318 : *
319 : * The function returns -1 if an error occurs. The error is available in
320 : * errno as expected in the POSIX interface.
321 : *
322 : * \param[in,out] buf The buffer where the data is read.
323 : * \param[in] size The size of the buffer.
324 : *
325 : * \return The number of bytes read from the socket, or -1 on errors.
326 : */
327 0 : int tcp_client::read(char *buf, size_t size)
328 : {
329 0 : return static_cast<int>(::read(f_socket.get(), buf, size));
330 : }
331 :
332 :
333 : /** \brief Read one line.
334 : *
335 : * This function reads one line from the current location up to the next
336 : * '\\n' character. We do not have any special handling of the '\\r'
337 : * character.
338 : *
339 : * The function may return 0 in which case the server closed the connection.
340 : *
341 : * \param[out] line The resulting line read from the server.
342 : *
343 : * \return The number of bytes read from the socket, or -1 on errors.
344 : * If the function returns 0 or more, then the \p line parameter
345 : * represents the characters read on the network.
346 : */
347 0 : int tcp_client::read_line(std::string& line)
348 : {
349 0 : line.clear();
350 0 : int len(0);
351 : for(;;)
352 : {
353 0 : char c;
354 0 : int r(read(&c, sizeof(c)));
355 0 : if(r <= 0)
356 : {
357 0 : return len == 0 && r < 0 ? -1 : len;
358 : }
359 0 : if(c == '\n')
360 : {
361 0 : return len;
362 : }
363 0 : ++len;
364 0 : line += c;
365 0 : }
366 : }
367 :
368 :
369 : /** \brief Write data to the socket.
370 : *
371 : * A TCP socket is a stream type of socket and one can write data to it
372 : * as if it were a regular file. This function writes \p size bytes to
373 : * the socket and then returns. This function returns early if the server
374 : * closes the connection.
375 : *
376 : * If your socket is not blocking, less than \p size bytes may be written
377 : * to the socket. In that case you are responsible for calling the function
378 : * again to write the remainder of the buffer until the function returns
379 : * a number of bytes written equal to \p size.
380 : *
381 : * The function returns -1 if an error occurs. The error is available in
382 : * errno as expected in the POSIX interface.
383 : *
384 : * \param[in] buf The buffer with the data to send over the socket.
385 : * \param[in] size The number of bytes in buffer to send over the socket.
386 : *
387 : * \return The number of bytes that were actually accepted by the socket
388 : * or -1 if an error occurs.
389 : */
390 0 : int tcp_client::write(const char *buf, size_t size)
391 : {
392 0 : return static_cast<int>(::write(f_socket.get(), buf, size));
393 : }
394 :
395 :
396 :
397 6 : } // namespace ed
398 : // vim: ts=4 sw=4 et
|