Line data Source code
1 : // Copyright (c) 2012-2019 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // This program is free software; you can redistribute it and/or modify
4 : // it under the terms of the GNU General Public License as published by
5 : // the Free Software Foundation; either version 2 of the License, or
6 : // (at your option) any later version.
7 : //
8 : // This program is distributed in the hope that it will be useful,
9 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 : // GNU General Public License for more details.
12 : //
13 : // You should have received a copy of the GNU General Public License
14 : // along with this program; if not, write to the Free Software
15 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16 :
17 : /** \file
18 : * \brief Implementation of the Snap Communicator class.
19 : *
20 : * This class wraps the C poll() interface in a C++ object with many types
21 : * of objects:
22 : *
23 : * \li Server Connections; for software that want to offer a port to
24 : * which clients can connect to; the server will call accept()
25 : * once a new client connection is ready; this results in a
26 : * Server/Client connection object
27 : * \li Client Connections; for software that want to connect to
28 : * a server; these expect the IP address and port to connect to
29 : * \li Server/Client Connections; for the server when it accepts a new
30 : * connection; in this case the server gets a socket from accept()
31 : * and creates one of these objects to handle the connection
32 : *
33 : * Using the poll() function is the easiest and allows us to listen
34 : * on pretty much any number of sockets (on my server it is limited
35 : * at 16,768 and frankly over 1,000 we probably will start to have
36 : * real slowness issues on small VPN servers.)
37 : */
38 :
39 : // to get the POLLRDHUP definition
40 : #ifndef _GNU_SOURCE
41 : #define _GNU_SOURCE
42 : #endif
43 :
44 :
45 : // self
46 : //
47 : #include "eventdispatcher/udp_server_message_connection.h"
48 :
49 : #include "eventdispatcher/exception.h"
50 : #include "eventdispatcher/udp_client.h"
51 :
52 :
53 : // snaplogger lib
54 : //
55 : #include "snaplogger/message.h"
56 :
57 :
58 : //// snapdev lib
59 : ////
60 : //#include "snapdev/not_reached.h"
61 : //#include "snapdev/not_used.h"
62 : //#include "snapdev/string_replace_many.h"
63 :
64 :
65 : // boost lib
66 : //
67 : #include <boost/preprocessor/stringize.hpp>
68 :
69 :
70 : //// C++ lib
71 : ////
72 : //#include <sstream>
73 : //#include <limits>
74 : //#include <atomic>
75 : //
76 : //
77 : //// C lib
78 : ////
79 : //#include <fcntl.h>
80 : //#include <poll.h>
81 : //#include <unistd.h>
82 : //#include <sys/eventfd.h>
83 : //#include <sys/inotify.h>
84 : //#include <sys/ioctl.h>
85 : //#include <sys/resource.h>
86 : //#include <sys/syscall.h>
87 : //#include <sys/time.h>
88 :
89 :
90 : // last include
91 : //
92 : #include <snapdev/poison.h>
93 :
94 :
95 :
96 : namespace ed
97 : {
98 :
99 :
100 :
101 : /** \brief Initialize a UDP server to send and receive messages.
102 : *
103 : * This function initialises a UDP server as a Snap UDP server
104 : * connection attached to the specified address and port.
105 : *
106 : * It is expected to be used to send and receive UDP messages.
107 : *
108 : * Note that to send messages, you need the address and port
109 : * of the destination. In effect, we do not use this server
110 : * when sending. Instead we create a client that we immediately
111 : * destruct once the message was sent.
112 : *
113 : * \param[in] addr The address to listen on.
114 : * \param[in] port The port to listen on.
115 : */
116 0 : udp_server_message_connection::udp_server_message_connection(std::string const & addr, int port)
117 0 : : udp_server_connection(addr, port)
118 : {
119 : // allow for looping over all the messages in one go
120 : //
121 0 : non_blocking();
122 0 : }
123 :
124 :
125 : /** \brief Send a UDP message.
126 : *
127 : * This function offers you to send a UDP message to the specified
128 : * address and port. The message should be small enough to fit in
129 : * on UDP packet or the call will fail.
130 : *
131 : * \note
132 : * The function return true when the message was successfully sent.
133 : * This does not mean it was received.
134 : *
135 : * \param[in] addr The destination address for the message.
136 : * \param[in] port The destination port for the message.
137 : * \param[in] message The message to send to the destination.
138 : * \param[in] secret_code The secret code to send along the message.
139 : *
140 : * \return true when the message was sent, false otherwise.
141 : */
142 0 : bool udp_server_message_connection::send_message(
143 : std::string const & addr
144 : , int port
145 : , message const & msg
146 : , std::string const & secret_code)
147 : {
148 : // Note: contrary to the TCP version, a UDP message does not
149 : // need to include the '\n' character since it is sent
150 : // in one UDP packet. However, it has a maximum size
151 : // limit which we enforce here.
152 : //
153 0 : udp_client client(addr, port);
154 0 : std::string buf;
155 0 : if(!secret_code.empty())
156 : {
157 0 : message m(msg);
158 0 : m.add_parameter("udp_secret", secret_code);
159 0 : buf = m.to_message();
160 : }
161 : else
162 : {
163 0 : buf = msg.to_message();
164 : }
165 0 : if(buf.length() > DATAGRAM_MAX_SIZE)
166 : {
167 : // packet too large for our buffers
168 : //
169 : throw event_dispatcher_invalid_message(
170 : "message too large ("
171 0 : + std::to_string(buf.length())
172 0 : + " bytes) for a UDP server (max: "
173 0 : BOOST_PP_STRINGIZE(DATAGRAM_MAX_SIZE));
174 : }
175 0 : if(client.send(buf.data(), buf.length()) != static_cast<ssize_t>(buf.length())) // we do not send the '\0'
176 : {
177 : // TODO: add errno to message
178 : SNAP_LOG_ERROR
179 0 : << "udp_server_message_connection::send_message(): could not send UDP message.";
180 0 : return false;
181 : }
182 :
183 0 : return true;
184 : }
185 :
186 :
187 : /** \brief Implementation of the process_read() callback.
188 : *
189 : * This function reads the datagram we just received using the
190 : * recv() function. The size of the datagram cannot be more than
191 : * DATAGRAM_MAX_SIZE (1Kb at time of writing.)
192 : *
193 : * The message is then parsed and further processing is expected
194 : * to be accomplished in your implementation of process_message().
195 : *
196 : * The function actually reads as many pending datagrams as it can.
197 : */
198 0 : void udp_server_message_connection::process_read()
199 : {
200 : char buf[DATAGRAM_MAX_SIZE];
201 0 : for(;;)
202 : {
203 0 : ssize_t const r(recv(buf, sizeof(buf) / sizeof(buf[0])));
204 0 : if(r <= 0)
205 : {
206 0 : break;
207 : }
208 0 : std::string const udp_message(buf, r);
209 0 : message msg;
210 0 : if(msg.from_message(udp_message))
211 : {
212 0 : std::string const expected(get_secret_code());
213 0 : if(msg.has_parameter("udp_secret"))
214 : {
215 0 : std::string const secret(msg.get_parameter("udp_secret"));
216 0 : if(secret != expected)
217 : {
218 0 : if(!expected.empty())
219 : {
220 : // our secret code and the message secret code do not match
221 : //
222 : SNAP_LOG_ERROR
223 0 : << "the incoming message has an unexpected udp_secret code, message ignored.";
224 0 : return;
225 : }
226 :
227 : // the sender included a UDP secret code but we don't
228 : // require it so we emit a warning but still accept
229 : // the message
230 : //
231 : SNAP_LOG_WARNING
232 0 : << "no udp_secret=... parameter was expected (missing set_secret_code() call for this application?)";
233 : }
234 : }
235 0 : else if(!expected.empty())
236 : {
237 : // secret code is missing from incoming message
238 : //
239 : SNAP_LOG_ERROR
240 0 : << "the incoming message was expected to have udp_secret code, message ignored";
241 0 : return;
242 : }
243 :
244 : // we received a valid message, process it
245 : //
246 0 : dispatch_message(msg);
247 : }
248 : else
249 : {
250 : SNAP_LOG_ERROR
251 : << "udp_server_message_connection::process_read() was asked"
252 0 : " to process an invalid message ("
253 0 : << udp_message
254 0 : << ")";
255 : }
256 : }
257 : }
258 :
259 :
260 :
261 6 : } // namespace ed
262 : // vim: ts=4 sw=4 et
|