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