LCOV - code coverage report
Current view: top level - eventdispatcher - local_dgram_server_message_connection.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 30 87 34.5 %
Date: 2024-03-16 21:58:34 Functions: 3 6 50.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2012-2024  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
      34             : //
      35             : #include    <snaplogger/message.h>
      36             : 
      37             : 
      38             : // snapdev
      39             : //
      40             : #include    <snapdev/stringize.h>
      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 Unix address to listen on.
      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             :  * \param[in] client_address  A Unix address used to send replies. It cannot
      72             :  * be abstract if you do want a client (i.e. if abstract, no client is
      73             :  * created).
      74             :  * \param[in] service_name  The name of the service if it applies to this
      75             :  * connection (i.e. a connection to connect to the communicator daemon).
      76             :  */
      77           2 : local_dgram_server_message_connection::local_dgram_server_message_connection(
      78             :               addr::addr_unix const & address
      79             :             , bool sequential
      80             :             , bool close_on_exec
      81             :             , bool force_reuse_addr
      82             :             , addr::addr_unix const & client_address
      83           2 :             , std::string const & service_name)
      84             :     : local_dgram_server_connection(
      85             :           address
      86             :         , sequential
      87             :         , close_on_exec
      88             :         , force_reuse_addr)
      89           2 :     , connection_with_send_message(service_name)
      90             : {
      91             :     // allow for looping over all the messages in one go
      92             :     //
      93           2 :     non_blocking();
      94             : 
      95           2 :     if(!client_address.is_unnamed())
      96             :     {
      97           0 :         f_dgram_client = std::make_shared<local_dgram_client>(client_address);
      98             :     }
      99           2 : }
     100             : 
     101             : 
     102             : /** \brief Send a message.
     103             :  *
     104             :  * This function sends \p message to the other side.
     105             :  *
     106             :  * The \p cache parameter is here because it is present in the send_message()
     107             :  * of the connection_with_send_message class. It is not used by the UDP
     108             :  * implementation, however.
     109             :  *
     110             :  * \param[in,out] msg  The message to forward to the other side.
     111             :  * \param[in] cache  This flag is ignored.
     112             :  *
     113             :  * \return true if the message was sent successfully.
     114             :  */
     115           0 : bool local_dgram_server_message_connection::send_message(
     116             :           message & msg
     117             :         , bool cache)
     118             : {
     119           0 :     snapdev::NOT_USED(cache);
     120             : 
     121           0 :     return send_message(msg, get_secret_code());
     122             : }
     123             : 
     124             : 
     125             : /** \brief Send a message over to the client.
     126             :  *
     127             :  * This function sends a message to the client at the address specified in
     128             :  * the constructor.
     129             :  *
     130             :  * The advantage of using this function is that the server port is
     131             :  * automatically attached to the message through the reply_port
     132             :  * parameter. This is important if you are running an application
     133             :  * which is not itself the main server (since the UDP mechanism is
     134             :  * opposite to the TCP mechanism, clients have to create servers
     135             :  * which have to listen and on one computer, multiple clients
     136             :  * would require you to assign additional ports to clients, which
     137             :  * is unusual).
     138             :  *
     139             :  * \exception initialization_missing
     140             :  * If no address was specified on the constructor (i.e. the ANY address
     141             :  * was used) then this exception is raised.
     142             :  *
     143             :  * \param[in] msg  The message to send to the client.
     144             :  * \param[in] secret_code  The secret code to attach to the message.
     145             :  *
     146             :  * \return true when the message was sent, false otherwise.
     147             :  */
     148           0 : bool local_dgram_server_message_connection::send_message(
     149             :           message const & msg
     150             :         , std::string const & secret_code)
     151             : {
     152           0 :     if(f_dgram_client == nullptr)
     153             :     {
     154           0 :         throw initialization_missing("this UDP server was not initialized with a client (see constructor).");
     155             :     }
     156             : 
     157           0 :     message with_address(msg);
     158           0 :     with_address.add_parameter("reply_to", get_address());
     159             : 
     160           0 :     return send_message(*f_dgram_client, msg, secret_code);
     161           0 : }
     162             : 
     163             : 
     164             : /** \brief Send a UDP message.
     165             :  *
     166             :  * This function offers you to send a UDP message to the specified
     167             :  * address and port. The message should be small enough to fit in
     168             :  * one UDP packet or the call will fail.
     169             :  *
     170             :  * \note
     171             :  * The function returns true when the message was successfully sent.
     172             :  * This does not mean it was received.
     173             :  *
     174             :  * \param[in] address  The destination Unix address for the message.
     175             :  * \param[in] msg  The message to send to the destination.
     176             :  * \param[in] secret_code  The secret code to send along the message.
     177             :  *
     178             :  * \return true when the message was sent, false otherwise.
     179             :  */
     180           3 : bool local_dgram_server_message_connection::send_message(
     181             :                   addr::addr_unix const & address
     182             :                 , message & msg
     183             :                 , std::string const & secret_code)
     184             : {
     185             :     // Note: contrary to the Stream version, a Datagram message does not
     186             :     //       need to include the '\n' character since it is sent
     187             :     //       in one packet. However, it has a maximum size limit
     188             :     //       which we enforce here.
     189             :     //
     190           3 :     std::string buf;
     191           3 :     if(!secret_code.empty())
     192             :     {
     193           0 :         message m(msg);
     194           0 :         m.add_parameter("secret_code", secret_code);
     195           0 :         buf = m.to_message();
     196           0 :     }
     197             :     else
     198             :     {
     199           3 :         buf = msg.to_message();
     200             :     }
     201             : 
     202             :     // TODO: this maximum size needs to be checked dynamically
     203             :     //
     204           3 :     if(buf.length() > DATAGRAM_MAX_SIZE)
     205             :     {
     206             :         // packet too large for the socket buffers
     207             :         //
     208           0 :         throw invalid_message(
     209             :                   "message too large ("
     210           0 :                 + std::to_string(buf.length())
     211           0 :                 + " bytes) for a Unix socket (max: "
     212             :                   SNAPDEV_STRINGIZE(DATAGRAM_MAX_SIZE)
     213           0 :                   ")");
     214             :     }
     215             : 
     216           3 :     local_dgram_client client(address);
     217           3 :     int const r(client.send(buf.data(), buf.length()));
     218           3 :     if(r != static_cast<ssize_t>(buf.length())) // we do not send the '\0'
     219             :     {
     220             :         // TODO: add errno to message
     221           0 :         int const e(errno);
     222           0 :         SNAP_LOG_ERROR
     223             :             << "local_dgram_server_message_connection::send_message(): could not send Datagram message to \""
     224           0 :             << address.to_uri()
     225           0 :             << "\", errno: "
     226             :             << e
     227             :             << ", "
     228           0 :             << strerror(e)
     229           0 :             << " (r = "
     230             :             << r
     231             :             << ")."
     232             :             << SNAP_LOG_SEND;
     233           0 :         errno = e;
     234           0 :         return false;
     235             :     }
     236             : 
     237           3 :     return true;
     238           3 : }
     239             : 
     240             : 
     241           0 : bool local_dgram_server_message_connection::send_message(
     242             :       local_dgram_client & client
     243             :     , message const & msg
     244             :     , std::string const & secret_code)
     245             : {
     246           0 :     std::string buf;
     247           0 :     if(!secret_code.empty())
     248             :     {
     249           0 :         message m(msg);
     250           0 :         m.add_parameter("secret_code", secret_code);
     251           0 :         buf = m.to_message();
     252           0 :     }
     253             :     else
     254             :     {
     255           0 :         buf = msg.to_message();
     256             :     }
     257             : 
     258             :     // TODO: this maximum size needs to be checked dynamically;
     259             :     //       also it's not forbidden to send a multiple packet
     260             :     //       UDP buffer, it's just more likely to fail
     261             :     //
     262           0 :     if(buf.length() > DATAGRAM_MAX_SIZE)
     263             :     {
     264             :         // packet too large for our buffers
     265             :         //
     266           0 :         throw invalid_message(
     267             :                   "message too large ("
     268           0 :                 + std::to_string(buf.length())
     269           0 :                 + " bytes) for a UDP server (max: "
     270             :                   SNAPDEV_STRINGIZE(DATAGRAM_MAX_SIZE)
     271           0 :                   ")");
     272             :     }
     273             : 
     274           0 :     if(client.send(buf.data(), buf.length()) != static_cast<ssize_t>(buf.length())) // we do not send the '\0'
     275             :     {
     276           0 :         int const e(errno);
     277           0 :         SNAP_LOG_ERROR
     278           0 :             << SNAP_LOG_FIELD("errno", std::to_string(e))
     279             :             << "udp_server_message_connection::send_message(): could not send UDP message."
     280             :             << SNAP_LOG_SEND;
     281           0 :         return false;
     282             :     }
     283             : 
     284           0 :     return true;
     285           0 : }
     286             : 
     287             : 
     288             : /** \brief Implementation of the process_read() callback.
     289             :  *
     290             :  * This function reads the datagram we just received using the
     291             :  * recv() function. The size of the datagram cannot be more than
     292             :  * DATAGRAM_MAX_SIZE (1Kb at time of writing.)
     293             :  *
     294             :  * The message is then parsed and further processing is expected
     295             :  * to be accomplished in your implementation of process_message().
     296             :  *
     297             :  * The function actually reads as many pending datagrams as it can.
     298             :  */
     299           3 : void local_dgram_server_message_connection::process_read()
     300             : {
     301           3 :     char buf[DATAGRAM_MAX_SIZE];
     302             :     for(;;)
     303             :     {
     304           6 :         ssize_t const r(recv(buf, sizeof(buf) / sizeof(buf[0])));
     305           6 :         if(r <= 0)
     306             :         {
     307           3 :             break;
     308             :         }
     309           6 :         std::string const local_dgram_message(buf, r);
     310           3 :         message msg;
     311           3 :         if(msg.from_message(local_dgram_message))
     312             :         {
     313           3 :             std::string const expected(get_secret_code());
     314           3 :             if(msg.has_parameter("secret_code"))
     315             :             {
     316           0 :                 std::string const secret(msg.get_parameter("secret_code"));
     317           0 :                 if(secret != expected)
     318             :                 {
     319           0 :                     if(!expected.empty())
     320             :                     {
     321             :                         // our secret code and the message secret code do not match
     322             :                         //
     323           0 :                         SNAP_LOG_ERROR
     324             :                             << "the incoming message has an unexpected secret_code code, message ignored."
     325             :                             << SNAP_LOG_SEND;
     326           0 :                         return;
     327             :                     }
     328             : 
     329             :                     // the sender included a UDP secret code but we don't
     330             :                     // require it so we emit a warning but still accept
     331             :                     // the message
     332             :                     //
     333           0 :                     SNAP_LOG_WARNING
     334             :                         << "no secret_code=... parameter was expected (missing set_secret_code() call for this application?)."
     335             :                         << SNAP_LOG_SEND;
     336             :                 }
     337           0 :             }
     338           3 :             else if(!expected.empty())
     339             :             {
     340             :                 // secret code is missing from incoming message
     341             :                 //
     342           0 :                 SNAP_LOG_ERROR
     343             :                     << "the incoming message was expected to have a secret_code parameter, message dropped."
     344             :                     << SNAP_LOG_SEND;
     345           0 :                 return;
     346             :             }
     347             : 
     348             :             // we received a valid message, process it
     349             :             //
     350           3 :             dispatch_message(msg);
     351           3 :         }
     352             :         else
     353             :         {
     354           0 :             SNAP_LOG_ERROR
     355             :                 << "local_dgram_server_message_connection::process_read() was"
     356             :                    " asked to process an invalid message ("
     357             :                 << local_dgram_message
     358             :                 << ")"
     359             :                 << SNAP_LOG_SEND;
     360             :         }
     361           6 :     }
     362             : }
     363             : 
     364             : 
     365             : 
     366             : } // namespace ed
     367             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.14

Snap C++ | List of projects | List of versions