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 Implementation of the Snap Communicator class.
22 : *
23 : * This class wraps the C poll() interface in a C++ object with many types
24 : * of objects:
25 : *
26 : * \li Server Connections; for software that want to offer a port to
27 : * which clients can connect to; the server will call accept()
28 : * once a new client connection is ready; this results in a
29 : * Server/Client connection object
30 : * \li Client Connections; for software that want to connect to
31 : * a server; these expect the IP address and port to connect to
32 : * \li Server/Client Connections; for the server when it accepts a new
33 : * connection; in this case the server gets a socket from accept()
34 : * and creates one of these objects to handle the connection
35 : *
36 : * Using the poll() function is the easiest and allows us to listen
37 : * on pretty much any number of sockets (on my server it is limited
38 : * at 16,768 and frankly over 1,000 we probably will start to have
39 : * real slowness issues on small VPN servers.)
40 : */
41 :
42 : // self
43 : //
44 : #include "eventdispatcher/udp_server_message_connection.h"
45 :
46 : #include "eventdispatcher/exception.h"
47 : #include "eventdispatcher/udp_client.h"
48 :
49 :
50 : // snaplogger lib
51 : //
52 : #include <snaplogger/message.h>
53 :
54 :
55 : // boost lib
56 : //
57 : #include <boost/preprocessor/stringize.hpp>
58 :
59 :
60 : // last include
61 : //
62 : #include <snapdev/poison.h>
63 :
64 :
65 :
66 : namespace ed
67 : {
68 :
69 :
70 :
71 : /** \brief Initialize a UDP server to send and receive messages.
72 : *
73 : * This function initialises a UDP server as a Snap UDP server
74 : * connection attached to the specified address and port.
75 : *
76 : * It is expected to be used to send and receive UDP messages.
77 : *
78 : * Note that to send messages, you need the address and port
79 : * of the destination. In effect, we do not use this server
80 : * when sending. Instead we create a client that we immediately
81 : * destruct once the message was sent.
82 : *
83 : * \param[in] addr The address to listen on.
84 : * \param[in] port The port to listen on.
85 : */
86 0 : udp_server_message_connection::udp_server_message_connection(std::string const & addr, int port)
87 0 : : udp_server_connection(addr, port)
88 : {
89 : // allow for looping over all the messages in one go
90 : //
91 0 : non_blocking();
92 0 : }
93 :
94 :
95 : /** \brief Send a UDP message.
96 : *
97 : * This function offers you to send a UDP message to the specified
98 : * address and port. The message should be small enough to fit in
99 : * on UDP packet or the call will fail.
100 : *
101 : * \note
102 : * The function return true when the message was successfully sent.
103 : * This does not mean it was received.
104 : *
105 : * \param[in] addr The destination address for the message.
106 : * \param[in] port The destination port for the message.
107 : * \param[in] message The message to send to the destination.
108 : * \param[in] secret_code The secret code to send along the message.
109 : *
110 : * \return true when the message was sent, false otherwise.
111 : */
112 0 : bool udp_server_message_connection::send_message(
113 : std::string const & addr
114 : , int port
115 : , message const & msg
116 : , std::string const & secret_code)
117 : {
118 : // Note: contrary to the TCP version, a UDP message does not
119 : // need to include the '\n' character since it is sent
120 : // in one UDP packet. However, it has a maximum size
121 : // limit which we enforce here.
122 : //
123 0 : udp_client client(addr, port);
124 0 : std::string buf;
125 0 : if(!secret_code.empty())
126 : {
127 0 : message m(msg);
128 0 : m.add_parameter("udp_secret", secret_code);
129 0 : buf = m.to_message();
130 : }
131 : else
132 : {
133 0 : buf = msg.to_message();
134 : }
135 0 : if(buf.length() > DATAGRAM_MAX_SIZE)
136 : {
137 : // packet too large for our buffers
138 : //
139 : throw event_dispatcher_invalid_message(
140 : "message too large ("
141 0 : + std::to_string(buf.length())
142 0 : + " bytes) for a UDP server (max: "
143 0 : BOOST_PP_STRINGIZE(DATAGRAM_MAX_SIZE));
144 : }
145 0 : if(client.send(buf.data(), buf.length()) != static_cast<ssize_t>(buf.length())) // we do not send the '\0'
146 : {
147 : // TODO: add errno to message
148 : SNAP_LOG_ERROR
149 0 : << "udp_server_message_connection::send_message(): could not send UDP message.";
150 0 : return false;
151 : }
152 :
153 0 : return true;
154 : }
155 :
156 :
157 : /** \brief Implementation of the process_read() callback.
158 : *
159 : * This function reads the datagram we just received using the
160 : * recv() function. The size of the datagram cannot be more than
161 : * DATAGRAM_MAX_SIZE (1Kb at time of writing.)
162 : *
163 : * The message is then parsed and further processing is expected
164 : * to be accomplished in your implementation of process_message().
165 : *
166 : * The function actually reads as many pending datagrams as it can.
167 : */
168 0 : void udp_server_message_connection::process_read()
169 : {
170 : char buf[DATAGRAM_MAX_SIZE];
171 0 : for(;;)
172 : {
173 0 : ssize_t const r(recv(buf, sizeof(buf) / sizeof(buf[0])));
174 0 : if(r <= 0)
175 : {
176 0 : break;
177 : }
178 0 : std::string const udp_message(buf, r);
179 0 : message msg;
180 0 : if(msg.from_message(udp_message))
181 : {
182 0 : std::string const expected(get_secret_code());
183 0 : if(msg.has_parameter("udp_secret"))
184 : {
185 0 : std::string const secret(msg.get_parameter("udp_secret"));
186 0 : if(secret != expected)
187 : {
188 0 : if(!expected.empty())
189 : {
190 : // our secret code and the message secret code do not match
191 : //
192 : SNAP_LOG_ERROR
193 0 : << "the incoming message has an unexpected udp_secret code, message ignored.";
194 0 : return;
195 : }
196 :
197 : // the sender included a UDP secret code but we don't
198 : // require it so we emit a warning but still accept
199 : // the message
200 : //
201 : SNAP_LOG_WARNING
202 0 : << "no udp_secret=... parameter was expected (missing set_secret_code() call for this application?)";
203 : }
204 : }
205 0 : else if(!expected.empty())
206 : {
207 : // secret code is missing from incoming message
208 : //
209 : SNAP_LOG_ERROR
210 0 : << "the incoming message was expected to have udp_secret code, message ignored";
211 0 : return;
212 : }
213 :
214 : // we received a valid message, process it
215 : //
216 0 : dispatch_message(msg);
217 : }
218 : else
219 : {
220 : SNAP_LOG_ERROR
221 : << "udp_server_message_connection::process_read() was asked"
222 0 : " to process an invalid message ("
223 0 : << udp_message
224 0 : << ")";
225 : }
226 : }
227 : }
228 :
229 :
230 :
231 6 : } // namespace ed
232 : // vim: ts=4 sw=4 et
|