LCOV - code coverage report
Current view: top level - eventdispatcher - udp_server_message_connection.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1 59 1.7 %
Date: 2022-06-18 10:10:36 Functions: 2 7 28.6 %
Legend: Lines: hit not hit

          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
      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 UDP server class.
      22             :  *
      23             :  * This class is used to listen and optionally send UDP messages.
      24             :  *
      25             :  * By default, the class only creates a server. With UDP, since it is state
      26             :  * less, the only way to communicate is via two servers and two clients.
      27             :  * A client is used to send messages and a server is used to listen and
      28             :  * receive messages.
      29             :  *
      30             :  * Since the port for the server and the client need to be different.
      31             :  * You may assign the server port 0 in which case it is automatically
      32             :  * generated and that port can be sent to the other side so that other
      33             :  * side can then reply to our messages.
      34             :  */
      35             : 
      36             : // self
      37             : //
      38             : #include    "eventdispatcher/udp_server_message_connection.h"
      39             : 
      40             : #include    "eventdispatcher/exception.h"
      41             : #include    "eventdispatcher/udp_client.h"
      42             : 
      43             : 
      44             : // snaplogger
      45             : //
      46             : #include    <snaplogger/message.h>
      47             : 
      48             : 
      49             : // libaddr
      50             : //
      51             : #include    <libaddr/iface.h>
      52             : 
      53             : 
      54             : // boost
      55             : //
      56             : #include    <boost/preprocessor/stringize.hpp>
      57             : 
      58             : 
      59             : // last include
      60             : //
      61             : #include    <snapdev/poison.h>
      62             : 
      63             : 
      64             : 
      65             : namespace ed
      66             : {
      67             : 
      68             : 
      69             : 
      70             : /** \brief Initialize a UDP server to send and receive messages.
      71             :  *
      72             :  * This function initializes a UDP server as a Snap UDP server
      73             :  * connection attached to the specified address and port.
      74             :  *
      75             :  * It is expected to be used to send and receive UDP messages.
      76             :  *
      77             :  * Note that to send messages, you need the address and port
      78             :  * of the destination. In effect, we do not use this server
      79             :  * when sending. Instead we create a client that we immediately
      80             :  * destruct once the message was sent.
      81             :  *
      82             :  * The \p client_address, if not set to ANY (0.0.0.0 or ::) is
      83             :  * used to create a udp_client object. That object is used
      84             :  * by the send_message() function. It also allows you to use
      85             :  * port 0 for the server which means you do not have to have
      86             :  * a reserved port for the server. That port can then be sent
      87             :  * to the client which can use it to send you replies.
      88             :  *
      89             :  * \param[in] server_address  The address and port to listen on.
      90             :  * \param[in] client_address  The address for the client side.
      91             :  */
      92           0 : udp_server_message_connection::udp_server_message_connection(
      93             :           addr::addr const & server_address
      94           0 :         , addr::addr const & client_address)
      95           0 :     : udp_server_connection(server_address)
      96             : {
      97             :     // allow for looping over all the messages in one go
      98             :     //
      99           0 :     non_blocking();
     100             : 
     101           0 :     if(client_address.get_network_type() != addr::addr::network_type_t::NETWORK_TYPE_ANY)
     102             :     {
     103           0 :         f_udp_client = std::make_shared<ed::udp_client>(client_address);
     104             :     }
     105           0 : }
     106             : 
     107             : 
     108             : /** \brief Send a message over to the client.
     109             :  *
     110             :  * This function sends a message to the client at the address specified in
     111             :  * the constructor.
     112             :  *
     113             :  * The advantage of using this function is that the server port is
     114             :  * automatically attached to the message through the reply_port
     115             :  * parameter. This is important if you are running an application
     116             :  * which is not itself the main server (since the UDP mechanism is
     117             :  * opposite to the TCP mechanism, clients have to create servers
     118             :  * which have to listen and on one computer, multiple clients
     119             :  * would require you to assign additional ports to clients, which
     120             :  * is unusual).
     121             :  *
     122             :  * \exception initialization_missing
     123             :  * If no address was specified on the constructor (i.e. the ANY address
     124             :  * was used) then this exception is raised.
     125             :  *
     126             :  * \param[in] msg  The message to send to the client.
     127             :  * \param[in] secret_code  The secret code to attach to the message.
     128             :  *
     129             :  * \return true when the message was sent, false otherwise.
     130             :  */
     131           0 : bool udp_server_message_connection::send_message(
     132             :       message const & msg
     133             :     , std::string const & secret_code)
     134             : {
     135           0 :     if(f_udp_client == nullptr)
     136             :     {
     137           0 :         throw initialization_missing("this UDP server was not initialized with a client (see constructor).");
     138             :     }
     139             : 
     140           0 :     message with_address(msg);
     141           0 :     with_address.add_parameter("reply_to", get_address());
     142             : 
     143           0 :     return send_message(*f_udp_client, msg, secret_code);
     144             : }
     145             : 
     146             : 
     147             : /** \brief Send a UDP message.
     148             :  *
     149             :  * This function offers you to send a UDP message to the specified
     150             :  * address and port. The message should be small enough to fit in
     151             :  * one UDP packet or the call will fail.
     152             :  *
     153             :  * \note
     154             :  * The function returns true when the message was successfully sent.
     155             :  * This does not mean it was received.
     156             :  *
     157             :  * \param[in] client_address  The destination address and port for the message.
     158             :  * \param[in] msg  The message to send to the destination.
     159             :  * \param[in] secret_code  The secret code to send along the message.
     160             :  *
     161             :  * \return true when the message was sent, false otherwise.
     162             :  */
     163           0 : bool udp_server_message_connection::send_message(
     164             :           addr::addr const & client_address
     165             :         , message const & msg
     166             :         , std::string const & secret_code)
     167             : {
     168             :     // Note: contrary to the TCP version, a UDP message does not
     169             :     //       need to include the '\n' character since it is sent
     170             :     //       in one UDP packet. However, it has a maximum size
     171             :     //       limit which we enforce here.
     172             :     //
     173           0 :     udp_client client(client_address);
     174             : 
     175             :     // you should use the multi-cast
     176             :     //
     177             :     // TODO: also the is_broadcast_address() re-reads the list of interfaces
     178             :     //       from the kernel, which is _slow_ (i.e. it doesn't get cached)
     179             :     //       See: libaddr/iface.cpp in the libaddr project
     180             :     //
     181           0 :     if(client_address.get_network_type() == addr::addr::network_type_t::NETWORK_TYPE_MULTICAST
     182           0 :     || addr::is_broadcast_address(client_address))
     183             :     {
     184           0 :         client.set_broadcast(true);
     185             :     }
     186             : 
     187           0 :     return send_message(client, msg, secret_code);
     188             : }
     189             : 
     190             : 
     191             : /** \brief Send a UDP message to the specified \p client.
     192             :  *
     193             :  * This function sends a UDP message to the specified client. In most
     194             :  * cases, you want to send a message using the other two send_message()
     195             :  * functions. If you have your own instance of a udp_client object,
     196             :  * then you are free to use this function instead.
     197             :  *
     198             :  * \todo
     199             :  * I think it would be possible to have this function as part of the
     200             :  * udp_client class instead. Since it is static, there is no real
     201             :  * need for any specific field from the UDP servero.
     202             :  *
     203             :  * \param[in] client  The client where the message gets sent.
     204             :  * \param[in] msg  The message to send to the destination.
     205             :  * \param[in] secret_code  The secret code to send along the message.
     206             :  *
     207             :  * \return true when the message was sent, false otherwise.
     208             :  */
     209           0 : bool udp_server_message_connection::send_message(
     210             :       udp_client & client
     211             :     , message const & msg
     212             :     , std::string const & secret_code)
     213             : {
     214           0 :     std::string buf;
     215           0 :     if(!secret_code.empty())
     216             :     {
     217           0 :         message m(msg);
     218           0 :         m.add_parameter("secret_code", secret_code);
     219           0 :         buf = m.to_message();
     220             :     }
     221             :     else
     222             :     {
     223           0 :         buf = msg.to_message();
     224             :     }
     225             : 
     226             :     // TODO: this maximum size needs to be checked dynamically;
     227             :     //       also it's not forbidden to send a multiple packet
     228             :     //       UDP buffer, it's just more likely to fail
     229             :     //
     230           0 :     if(buf.length() > DATAGRAM_MAX_SIZE)
     231             :     {
     232             :         // packet too large for our buffers
     233             :         //
     234             :         throw invalid_message(
     235             :                   "message too large ("
     236           0 :                 + std::to_string(buf.length())
     237           0 :                 + " bytes) for a UDP server (max: "
     238             :                   BOOST_PP_STRINGIZE(DATAGRAM_MAX_SIZE)
     239           0 :                   ")");
     240             :     }
     241             : 
     242           0 :     if(client.send(buf.data(), buf.length()) != static_cast<ssize_t>(buf.length())) // we do not send the '\0'
     243             :     {
     244           0 :         int const e(errno);
     245           0 :         SNAP_LOG_ERROR
     246           0 :             << SNAP_LOG_FIELD("errno", std::to_string(e))
     247             :             << "udp_server_message_connection::send_message(): could not send UDP message."
     248             :             << SNAP_LOG_SEND;
     249           0 :         return false;
     250             :     }
     251             : 
     252           0 :     return true;
     253             : }
     254             : 
     255             : 
     256             : /** \brief Implementation of the process_read() callback.
     257             :  *
     258             :  * This function reads the datagram we just received using the
     259             :  * recv() function. The size of the datagram cannot be more than
     260             :  * DATAGRAM_MAX_SIZE (1Kb at time of writing.)
     261             :  *
     262             :  * The message is then parsed and further processing is expected
     263             :  * to be accomplished in your implementation of process_message().
     264             :  *
     265             :  * The function actually reads as many pending datagrams as it can.
     266             :  */
     267           0 : void udp_server_message_connection::process_read()
     268             : {
     269           0 :     char buf[DATAGRAM_MAX_SIZE];
     270             :     for(;;)
     271             :     {
     272           0 :         ssize_t const r(recv(buf, sizeof(buf) / sizeof(buf[0])));
     273           0 :         if(r <= 0)
     274             :         {
     275           0 :             break;
     276             :         }
     277           0 :         std::string const udp_message(buf, r);
     278           0 :         message msg;
     279           0 :         if(msg.from_message(udp_message))
     280             :         {
     281           0 :             std::string const expected(get_secret_code());
     282           0 :             if(msg.has_parameter("secret_code"))
     283             :             {
     284           0 :                 std::string const secret(msg.get_parameter("secret_code"));
     285           0 :                 if(secret != expected)
     286             :                 {
     287           0 :                     if(!expected.empty())
     288             :                     {
     289             :                         // our secret code and the message secret code do not match
     290             :                         //
     291           0 :                         SNAP_LOG_ERROR
     292             :                             << "the incoming message has an unexpected secret_code code, message ignored."
     293             :                             << SNAP_LOG_SEND;
     294           0 :                         return;
     295             :                     }
     296             : 
     297             :                     // the sender included a UDP secret code but we don't
     298             :                     // require it so we emit a warning but still accept
     299             :                     // the message
     300             :                     //
     301           0 :                     SNAP_LOG_WARNING
     302             :                         << "no secret_code=... parameter was expected (missing set_secret_code() call for this application?)"
     303             :                         << SNAP_LOG_SEND;
     304             :                 }
     305             :             }
     306           0 :             else if(!expected.empty())
     307             :             {
     308             :                 // secret code is missing from incoming message
     309             :                 //
     310           0 :                 SNAP_LOG_ERROR
     311             :                     << "the incoming message was expected to have a secret_code parameter, message ignored."
     312             :                     << SNAP_LOG_SEND;
     313           0 :                 return;
     314             :             }
     315             : 
     316             :             // we received a valid message, process it
     317             :             //
     318           0 :             dispatch_message(msg);
     319             :         }
     320             :         else
     321             :         {
     322           0 :             SNAP_LOG_ERROR
     323             :                 << "udp_server_message_connection::process_read() was asked"
     324             :                    " to process an invalid message ("
     325             :                 << udp_message
     326             :                 << ")"
     327             :                 << SNAP_LOG_SEND;
     328             :         }
     329           0 :     }
     330             : }
     331             : 
     332             : 
     333             : 
     334           6 : } // namespace ed
     335             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13