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 : // to get the POLLRDHUP definition
27 : #ifndef _GNU_SOURCE
28 : #define _GNU_SOURCE
29 : #endif
30 :
31 :
32 : // self
33 : //
34 : #include "eventdispatcher/udp_server.h"
35 :
36 : #include "eventdispatcher/exception.h"
37 :
38 :
39 : // snapwebsites lib
40 : //
41 : #include <snaplogger/message.h>
42 :
43 :
44 : // C lib
45 : //
46 : #include <arpa/inet.h>
47 : #include <poll.h>
48 : #include <string.h>
49 :
50 :
51 : // last include
52 : //
53 : #include <snapdev/poison.h>
54 :
55 :
56 :
57 :
58 : namespace ed
59 : {
60 :
61 :
62 :
63 : /** \brief Initialize a UDP server object.
64 : *
65 : * This function initializes a UDP server object making it ready to
66 : * receive messages.
67 : *
68 : * The server address and port are specified in the constructor so
69 : * if you need to receive messages from several different addresses
70 : * and/or port, you'll have to create a server for each.
71 : *
72 : * The address is a string and it can represent an IPv4 or IPv6
73 : * address.
74 : *
75 : * Note that this function calls bind() to listen to the socket
76 : * at the specified address. To accept data on different UDP addresses
77 : * and ports, multiple UDP servers must be created.
78 : *
79 : * \note
80 : * The socket is open in this process. If you fork() or exec() then the
81 : * socket will be closed by the operating system.
82 : *
83 : * \warning
84 : * We only make use of the first address found by getaddrinfo(). All
85 : * the other addresses are ignored.
86 : *
87 : * \warning
88 : * Remember that the multicast feature under Linux is shared by all
89 : * processes running on that server. Any one process can listen for
90 : * any and all multicast messages from any other process. Our
91 : * implementation limits the multicast from a specific IP. However.
92 : * other processes can also receive your packets and there is nothing
93 : * you can do to prevent that.
94 : *
95 : * \exception udp_client_server_runtime_error
96 : * The udp_client_server_runtime_error exception is raised when the address
97 : * and port combination cannot be resolved or if the socket cannot be
98 : * opened.
99 : *
100 : * \param[in] addr The address we receive on.
101 : * \param[in] port The port we receive from.
102 : * \param[in] family The family used to search for 'addr'.
103 : * \param[in] multicast_addr A multicast address.
104 : */
105 0 : udp_server::udp_server(std::string const & addr, int port, int family, std::string const * multicast_addr)
106 0 : : udp_base(addr, port, family)
107 : {
108 0 : int r(0);
109 0 : raii_addrinfo_t multicast_addrinfo;
110 :
111 0 : std::stringstream decimal_port;
112 0 : decimal_port << f_port;
113 0 : std::string port_str(decimal_port.str());
114 :
115 0 : if(multicast_addr != nullptr)
116 : {
117 : // in multicast we have to bind to the multicast IP (or IN_ANYADDR
118 : // which right now we do not support)
119 : //
120 0 : addrinfo hints = addrinfo();
121 0 : hints.ai_family = AF_UNSPEC;
122 0 : hints.ai_socktype = SOCK_DGRAM;
123 0 : hints.ai_protocol = IPPROTO_UDP;
124 :
125 : // we use the multicast address, but the same port as for
126 : // the other address
127 : //
128 0 : addrinfo * a(nullptr);
129 0 : r = getaddrinfo(multicast_addr->c_str(), port_str.c_str(), &hints, &a);
130 0 : if(r != 0 || a == nullptr)
131 : {
132 : throw event_dispatcher_runtime_error(
133 : "invalid address or port for UDP multicast socket: \""
134 0 : + *multicast_addr
135 0 : + "\"");
136 : }
137 0 : multicast_addrinfo = raii_addrinfo_t(a);
138 :
139 0 : if(multicast_addrinfo->ai_family != AF_INET
140 0 : || f_addrinfo->ai_family != AF_INET)
141 : {
142 : throw event_dispatcher_runtime_error(
143 : "the UDP multicast implementation only supports IPv4 at the moment: \""
144 0 : + *multicast_addr
145 0 : + "\"");
146 : }
147 :
148 0 : r = bind(f_socket.get(), multicast_addrinfo->ai_addr, multicast_addrinfo->ai_addrlen);
149 : }
150 : else
151 : {
152 : // bind to the very first address
153 : //
154 0 : r = bind(f_socket.get(), f_addrinfo->ai_addr, f_addrinfo->ai_addrlen);
155 : }
156 :
157 0 : if(r != 0)
158 : {
159 0 : int const e(errno);
160 :
161 : // reverse the address from the f_addrinfo so we know exactly
162 : // which one was picked
163 : //
164 0 : char addr_buf[256];
165 0 : switch(f_addrinfo->ai_family)
166 : {
167 0 : case AF_INET:
168 0 : inet_ntop(AF_INET
169 0 : , &reinterpret_cast<struct sockaddr_in *>(f_addrinfo->ai_addr)->sin_addr
170 : , addr_buf
171 : , sizeof(addr_buf));
172 0 : break;
173 :
174 0 : case AF_INET6:
175 0 : inet_ntop(AF_INET6
176 0 : , &reinterpret_cast<struct sockaddr_in6 *>(f_addrinfo->ai_addr)->sin6_addr
177 : , addr_buf
178 : , sizeof(addr_buf));
179 0 : break;
180 :
181 0 : default:
182 0 : strncpy(addr_buf, "Unknown Address Family", sizeof(addr_buf));
183 0 : break;
184 :
185 : }
186 :
187 0 : SNAP_LOG_ERROR
188 0 : << "the bind() function failed with errno: "
189 : << e
190 : << " ("
191 0 : << strerror(e)
192 0 : << "); address length "
193 0 : << f_addrinfo->ai_addrlen
194 : << " and address is \""
195 : << addr_buf
196 : << "\""
197 : << SNAP_LOG_SEND;
198 0 : throw event_dispatcher_runtime_error("could not bind UDP socket to \"" + f_addr + ":" + std::to_string(port) + "\"");
199 : }
200 :
201 : // are we creating a server to listen to multicast packets?
202 : //
203 0 : if(multicast_addrinfo != nullptr)
204 : {
205 0 : ip_mreqn mreq = {};
206 :
207 : // both addresses must have the right size
208 : //
209 0 : if(multicast_addrinfo->ai_addrlen <= sizeof(mreq.imr_multiaddr)
210 0 : || f_addrinfo->ai_addrlen <= sizeof(mreq.imr_address))
211 : {
212 : throw event_dispatcher_runtime_error(
213 : "invalid address type for UDP multicast: \""
214 0 : + addr + ":" + port_str
215 0 : + "\" or \""
216 0 : + *multicast_addr + ":" + port_str + "\" (sizes: "
217 0 : + std::to_string(multicast_addrinfo->ai_addrlen) + ", "
218 0 : + std::to_string(f_addrinfo->ai_addrlen) + ", "
219 0 : + std::to_string(sizeof(mreq.imr_address)) + ")");
220 : }
221 :
222 0 : memcpy(&mreq.imr_multiaddr, &reinterpret_cast<sockaddr_in *>(multicast_addrinfo->ai_addr)->sin_addr, sizeof(mreq.imr_multiaddr));
223 0 : memcpy(&mreq.imr_address, &reinterpret_cast<sockaddr_in *>(f_addrinfo->ai_addr)->sin_addr, sizeof(mreq.imr_address));
224 0 : mreq.imr_ifindex = 0; // no specific interface
225 :
226 0 : r = setsockopt(f_socket.get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
227 0 : if(r < 0)
228 : {
229 0 : int const e(errno);
230 : throw event_dispatcher_runtime_error(
231 : "IP_ADD_MEMBERSHIP failed for: \""
232 0 : + addr + ":" + port_str
233 0 : + "\" or \"" + *multicast_addr + ":" + port_str
234 0 : + "\", errno: "
235 0 : + std::to_string(e) + ", " + strerror(e));
236 : }
237 :
238 : // setup the multicast to 0 so we don't receive other's
239 : // messages; apparently the default would be 1
240 : //
241 0 : int multicast_all(0);
242 0 : r = setsockopt(f_socket.get(), IPPROTO_IP, IP_MULTICAST_ALL, &multicast_all, sizeof(multicast_all));
243 0 : if(r < 0)
244 : {
245 : // things should still work if the IP_MULTICAST_ALL is not
246 : // set as we want it
247 : //
248 0 : int const e(errno);
249 0 : SNAP_LOG_WARNING
250 0 : << "could not set IP_MULTICAST_ALL to zero, e = "
251 : << e
252 : << " -- "
253 0 : << strerror(e)
254 : << SNAP_LOG_SEND;
255 : }
256 : }
257 0 : }
258 :
259 :
260 : /** \brief Clean up the UDP server.
261 : *
262 : * This function frees the address info structures and close the socket.
263 : */
264 0 : udp_server::~udp_server()
265 : {
266 0 : }
267 :
268 :
269 : /** \brief Wait on a message.
270 : *
271 : * This function waits until a message is received on this UDP server.
272 : * There are no means to return from this function except by receiving
273 : * a message. Remember that UDP does not have a connect state so whether
274 : * another process quits does not change the status of this UDP server
275 : * and thus it continues to wait forever.
276 : *
277 : * Note that you may change the type of socket by making it non-blocking
278 : * (use the get_socket() to retrieve the socket identifier) in which
279 : * case this function will not block if no message is available. Instead
280 : * it returns immediately.
281 : *
282 : * \param[in] msg The buffer where the message is saved.
283 : * \param[in] max_size The maximum size the message (i.e. size of the \p msg buffer.)
284 : *
285 : * \return The number of bytes read or -1 if an error occurs.
286 : */
287 0 : int udp_server::recv(char * msg, size_t max_size)
288 : {
289 0 : return static_cast<int>(::recv(f_socket.get(), msg, max_size, 0));
290 : }
291 :
292 :
293 : /** \brief Wait for data to come in.
294 : *
295 : * This function waits for a given amount of time for data to come in. If
296 : * no data comes in after max_wait_ms, the function returns with -1 and
297 : * errno set to EAGAIN.
298 : *
299 : * The socket is expected to be a blocking socket (the default,) although
300 : * it is possible to setup the socket as non-blocking if necessary for
301 : * some other reason.
302 : *
303 : * This function blocks for a maximum amount of time as defined by
304 : * max_wait_ms. It may return sooner with an error or a message.
305 : *
306 : * \param[in] msg The buffer where the message will be saved.
307 : * \param[in] max_size The size of the \p msg buffer in bytes.
308 : * \param[in] max_wait_ms The maximum number of milliseconds to wait for a message.
309 : *
310 : * \return -1 if an error occurs or the function timed out, the number of bytes received otherwise.
311 : */
312 0 : int udp_server::timed_recv(char * msg, size_t const max_size, int const max_wait_ms)
313 : {
314 0 : pollfd fd;
315 0 : fd.events = POLLIN | POLLPRI | POLLRDHUP;
316 0 : fd.fd = f_socket.get();
317 0 : int const retval(poll(&fd, 1, max_wait_ms));
318 :
319 : // fd_set s;
320 : // FD_ZERO(&s);
321 : //#pragma GCC diagnostic push
322 : //#pragma GCC diagnostic ignored "-Wold-style-cast"
323 : // FD_SET(f_socket.get(), &s);
324 : //#pragma GCC diagnostic pop
325 : // struct timeval timeout;
326 : // timeout.tv_sec = max_wait_ms / 1000;
327 : // timeout.tv_usec = (max_wait_ms % 1000) * 1000;
328 : // int const retval(select(f_socket.get() + 1, &s, nullptr, &s, &timeout));
329 0 : if(retval == -1)
330 : {
331 : // poll() sets errno accordingly
332 0 : return -1;
333 : }
334 0 : if(retval > 0)
335 : {
336 : // our socket has data
337 0 : return static_cast<int>(::recv(f_socket.get(), msg, max_size, 0));
338 : }
339 :
340 : // our socket has no data
341 0 : errno = EAGAIN;
342 0 : return -1;
343 : }
344 :
345 :
346 : /** \brief Wait for data to come in, but return a std::string.
347 : *
348 : * This function waits for a given amount of time for data to come in. If
349 : * no data comes in after max_wait_ms, the function returns with -1 and
350 : * errno set to EAGAIN.
351 : *
352 : * The socket is expected to be a blocking socket (the default,) although
353 : * it is possible to setup the socket as non-blocking if necessary for
354 : * some other reason.
355 : *
356 : * This function blocks for a maximum amount of time as defined by
357 : * max_wait_ms. It may return sooner with an error or a message.
358 : *
359 : * \param[in] bufsize The maximum size of the returned string in bytes.
360 : * \param[in] max_wait_ms The maximum number of milliseconds to wait for a message.
361 : *
362 : * \return The received string or an empty string if not data received or error.
363 : *
364 : * \sa timed_recv()
365 : */
366 0 : std::string udp_server::timed_recv(int const bufsize, int const max_wait_ms)
367 : {
368 0 : std::vector<char> buf;
369 0 : buf.resize(bufsize + 1, '\0'); // +1 for ending \0
370 0 : int const r(timed_recv(&buf[0], bufsize, max_wait_ms));
371 0 : if(r <= -1)
372 : {
373 : // Timed out, so return empty string.
374 : // TBD: could std::string() smash errno?
375 : //
376 0 : return std::string();
377 : }
378 :
379 : // Resize the buffer, then convert to std string
380 : //
381 0 : buf.resize(r + 1, '\0');
382 :
383 0 : std::string word;
384 0 : word.resize(r);
385 0 : std::copy(buf.begin(), buf.end(), word.begin());
386 :
387 0 : return word;
388 : }
389 :
390 :
391 :
392 :
393 6 : } // namespace ed
394 : // vim: ts=4 sw=4 et
|