LCOV - code coverage report
Current view: top level - eventdispatcher - socket_events.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 2 234 0.9 %
Date: 2021-09-19 09:06:58 Functions: 2 29 6.9 %
Legend: Lines: hit not hit

          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 along
      17             : // with this program; if not, write to the Free Software Foundation, Inc.,
      18             : // 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      19             : 
      20             : /** \file
      21             :  * \brief Implementation of the socket events class.
      22             :  *
      23             :  * The Linux kernel offers an interface to listen to the network stack
      24             :  * called NETLINK. This can be used to detect the current state of the stack
      25             :  * without having to read the /proc file system. It is expected to be faster
      26             :  * than the other methods, although having a permanent TCP connection is
      27             :  * possibly even faster than using this class.
      28             :  *
      29             :  * We can make use of a single NETLINK, so we have an internal class which
      30             :  * is doing the heavy work. The socket_events connections you create actually
      31             :  * listen through that internal class.
      32             :  */
      33             : 
      34             : 
      35             : // self
      36             : //
      37             : #include    "eventdispatcher/socket_events.h"
      38             : 
      39             : #include    "eventdispatcher/communicator.h"
      40             : #include    "eventdispatcher/exception.h"
      41             : #include    "eventdispatcher/timer.h"
      42             : 
      43             : 
      44             : // snaplogger lib
      45             : //
      46             : #include    <snaplogger/message.h>
      47             : 
      48             : 
      49             : // snapdev lib
      50             : //
      51             : #include    <snapdev/raii_generic_deleter.h>
      52             : 
      53             : 
      54             : // cppthread lib
      55             : //
      56             : #include    <cppthread/guard.h>
      57             : #include    <cppthread/mutex.h>
      58             : 
      59             : 
      60             : // libaddr lib
      61             : //
      62             : #include    <libaddr/addr_parser.h>
      63             : 
      64             : 
      65             : // C++ lib
      66             : //
      67             : #include    <algorithm>
      68             : #include    <deque>
      69             : 
      70             : 
      71             : // C lib
      72             : //
      73             : #include    <linux/inet_diag.h>
      74             : #include    <linux/netlink.h>
      75             : #include    <linux/sock_diag.h>
      76             : 
      77             : 
      78             : // last include
      79             : //
      80             : #include    <snapdev/poison.h>
      81             : 
      82             : 
      83             : 
      84             : namespace ed
      85             : {
      86             : 
      87             : 
      88             : 
      89             : namespace
      90             : {
      91             : 
      92             : 
      93             : 
      94             : /** \brief Internal structure to hold socket_events objects.
      95             :  *
      96             :  * Each socket_events object linked to the socket_listener are saved
      97             :  * in this structure.
      98             :  *
      99             :  * \todo
     100             :  * Look into getting a shared pointer instead of the bare pointer to
     101             :  * the socket_events object. The idea being that the socket_events is
     102             :  * not ready for a shared pointer on construction. So I'm not too sure
     103             :  * how to handle that one.
     104             :  */
     105           0 : struct socket_evt
     106             : {
     107             :     typedef std::shared_ptr<socket_evt>     pointer_t;
     108             :     typedef std::deque<pointer_t>           deque_t;
     109             : 
     110             :     bool                        f_listening = false;
     111             :     socket_events *             f_socket_events = nullptr;
     112             : };
     113             : 
     114             : 
     115             : 
     116             : 
     117             : /** \brief Internal class used to handle the NETLINK socket.
     118             :  *
     119             :  * It is not a good idea to have many connections to NETLINK when it is
     120             :  * possible to have just one which allows us to send one request in one
     121             :  * message to get the status of all the sockets we are listening to.
     122             :  * For this reason, we have a single internal class listening to the
     123             :  * NETLINK messages.
     124             :  */
     125             : class socket_listener
     126             :     : public timer
     127             : {
     128             : public:
     129             :     typedef std::shared_ptr<socket_listener>        pointer_t;
     130             : 
     131             :     static constexpr std::size_t const  RECEIVE_BUFFER_SIZE = 1'000 * (sizeof(nlmsghdr) + sizeof(inet_diag_msg));
     132             :     static constexpr int                TCP_LISTEN_STATE = 10;
     133             : 
     134             :                                 socket_listener(cppthread::mutex & socket_mutex);
     135             :     virtual                     ~socket_listener();
     136             : 
     137             :     static pointer_t            instance();
     138             : 
     139             :     void                        add_socket_events(socket_events * evts);
     140             :     void                        lost_connection(socket_events * evts);
     141             :     void                        remove_socket_events(socket_events * evts);
     142             : 
     143             :     // connection implementation
     144             :     virtual bool                is_reader() const override;
     145             :     virtual bool                is_writer() const override;
     146             :     virtual int                 get_socket() const override;
     147             :     virtual void                process_timeout() override;
     148             :     virtual void                process_read() override;
     149             :     virtual void                process_write() override;
     150             :     virtual void                process_error() override;
     151             :     virtual void                process_hup() override;
     152             :     virtual void                process_invalid() override;
     153             : 
     154             : private:
     155             :     cppthread::mutex &          f_socket_mutex;
     156             :     snap::raii_fd_t             f_netlink_socket = snap::raii_fd_t();
     157             :     socket_evt::deque_t         f_socket_events = socket_evt::deque_t();
     158             : };
     159             : 
     160             : 
     161             : /** \brief Instance pointer of the socket_listener object.
     162             :  *
     163             :  * This pointer holds the socket_listener instance we use with all the
     164             :  * socket_events objects.
     165             :  */
     166           2 : socket_listener::pointer_t      g_socket_listener = socket_listener::pointer_t();
     167             : 
     168             : 
     169             : /** \brief Initialize the socket_listener.
     170             :  *
     171             :  * To create a socket_listener, you have to call the instance() function.
     172             :  * This function, though, is the one that actually creates the instance.
     173             :  * It opens a link to the NETLINK system of the Linux kernel. It also
     174             :  * increases the size of that connection buffer to make sure we can handle
     175             :  * all our messages in one go.
     176             :  *
     177             :  * \exception event_dispatcher_runtime_error
     178             :  * If the opening of the AF_NETLINK socket fails, then this exception is
     179             :  * raised.
     180             :  *
     181             :  * \param[in] socket_mutex  The mutex to use to lock various functions.
     182             :  */
     183           0 : socket_listener::socket_listener(cppthread::mutex & socket_mutex)
     184             :     : timer(1'000'000)
     185             :     , f_socket_mutex(socket_mutex)
     186             :     , f_netlink_socket(socket(
     187             :                   AF_NETLINK
     188             :                 , SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK
     189           0 :                 , NETLINK_SOCK_DIAG))
     190             : {
     191           0 :     if(f_netlink_socket < 0)
     192             :     {
     193           0 :         throw event_dispatcher_runtime_error("opening SOCK_RAW failed in socket_listener.");
     194             :     }
     195             : 
     196             :     // increase our changes to avoid memory issues
     197             :     //
     198           0 :     int const sndbuf(32 * 1'024);
     199           0 :     if(setsockopt(
     200           0 :               f_netlink_socket.get()
     201             :             , SOL_SOCKET
     202             :             , SO_SNDBUF
     203             :             , &sndbuf
     204           0 :             , sizeof(sndbuf)) != 0)
     205             :     {
     206           0 :         SNAP_LOG_WARNING
     207           0 :             << "the SO_SNDBUF failed against the NETLINK socket."
     208             :             << SNAP_LOG_SEND;
     209             :     }
     210             : 
     211             :     // enough space to support up to about 1,000 messages max.
     212             :     //
     213           0 :     int const rcvbuf(RECEIVE_BUFFER_SIZE);
     214           0 :     if(setsockopt(
     215           0 :               f_netlink_socket.get()
     216             :             , SOL_SOCKET
     217             :             , SO_RCVBUF
     218             :             , &rcvbuf
     219           0 :             , sizeof(rcvbuf)) != 0)
     220             :     {
     221           0 :         SNAP_LOG_WARNING
     222           0 :             << "the SO_RCVBUF failed against the NETLINK socket."
     223             :             << SNAP_LOG_SEND;
     224             :     }
     225             : 
     226             : #if 0
     227             :     struct sockaddr_nl addr = {};
     228             :     addr.nl_family = AF_NETLINK;
     229             :     addr.nl_pid = getpid();
     230             : 
     231             :     // You can find the "groups" flags in Linux source:
     232             :     // (change the version as required with your current version)
     233             :     //
     234             :     //     "/usr/src/linux-headers-4.15.0-147/include/net/tcp_states.h
     235             :     //
     236             :     addr.nl_groups = TCPF_LISTEN;
     237             : 
     238             :     if(bind(d, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) != 0)
     239             :     {
     240             :         throw event_dispatcher_runtime_error("could not bind() the SOCK_RAW of socket_listener.");
     241             :     }
     242             : #endif
     243           0 : }
     244             : 
     245             : 
     246             : /** \brief Handle the virtual table.
     247             :  *
     248             :  * This destructor is here primarily to handle the virtual table requirements.
     249             :  */
     250           0 : socket_listener::~socket_listener()
     251             : {
     252           0 : }
     253             : 
     254             : 
     255             : /** \brief Retrieve the instance of the socket_listener.
     256             :  *
     257             :  * You have a maximum of one socket_listener per process. It would be a
     258             :  * waste to have more than that.
     259             :  *
     260             :  * \note
     261             :  * We do not currently offer a way to ever delete the instance.
     262             :  */
     263           0 : socket_listener::pointer_t socket_listener::instance()
     264             : {
     265           0 :     static cppthread::mutex g_mutex;
     266             : 
     267           0 :     cppthread::guard g(g_mutex);
     268             : 
     269           0 :     if(g_socket_listener == nullptr)
     270             :     {
     271           0 :         g_socket_listener.reset(new socket_listener(g_mutex));
     272             : 
     273           0 :         communicator::instance()->add_connection(g_socket_listener);
     274             :     }
     275             : 
     276           0 :     return g_socket_listener;
     277             : }
     278             : 
     279             : 
     280             : /** \brief Add a socket_events object to our list.
     281             :  *
     282             :  * The listener manages a list of socket_events objects. This function is
     283             :  * used to add a socket_events object to that list. Once added, the object
     284             :  * receives socket events (i.e. calls to the process_listening() function).
     285             :  *
     286             :  * \param[in] evts  The events we want to listen to.
     287             :  */
     288           0 : void socket_listener::add_socket_events(socket_events * evts)
     289             : {
     290           0 :     if(!evts->get_addr().is_ipv4())
     291             :     {
     292           0 :         throw event_dispatcher_invalid_parameter("at this time, the socket listener is limited to IPv4 addresses.");
     293             :     }
     294             : 
     295           0 :     cppthread::guard g(f_socket_mutex);
     296             : 
     297           0 :     socket_evt::pointer_t evt(std::make_shared<socket_evt>());
     298           0 :     evt->f_socket_events = evts;
     299             : 
     300           0 :     f_socket_events.push_back(evt);
     301             : 
     302           0 :     set_enable(true);
     303           0 : }
     304             : 
     305             : 
     306             : /** \brief Signal the loss of a connection.
     307             :  *
     308             :  * Call this function whenever the client loses the connection to the
     309             :  * server. This tells the socket_listener that this client is not
     310             :  * connected anymore and thus that we should again listen for its
     311             :  * corresponding address and port when listening for socket events.
     312             :  *
     313             :  * \param[in] evts  The events we want to listen to again.
     314             :  */
     315           0 : void socket_listener::lost_connection(socket_events * evts)
     316             : {
     317           0 :     cppthread::guard g(f_socket_mutex);
     318             : 
     319           0 :     auto it(std::find_if(
     320           0 :               f_socket_events.begin()
     321           0 :             , f_socket_events.end()
     322           0 :             , [&evts](socket_evt::pointer_t evt)
     323           0 :             {
     324           0 :                 return evt->f_socket_events == evts;
     325           0 :             }));
     326           0 :     if(it != f_socket_events.end())
     327             :     {
     328             :         // if we lost the connection we assume that the other end is not
     329             :         // listening
     330             :         //
     331           0 :         (*it)->f_listening = false;
     332             :     }
     333             : 
     334           0 :     set_enable(true);
     335           0 : }
     336             : 
     337             : 
     338             : /** \brief Remove a socket_events object from our list.
     339             :  *
     340             :  * The listener manages a list of socket_events objects. This function is
     341             :  * used to remove a socket_events object from that list. Once removed,
     342             :  * the object will stop receiving events (i.e. calls to the process_listening()
     343             :  * function).
     344             :  *
     345             :  * \param[in] evts  The events we were listening to.
     346             :  */
     347           0 : void socket_listener::remove_socket_events(socket_events * evts)
     348             : {
     349           0 :     cppthread::guard g(f_socket_mutex);
     350             : 
     351           0 :     auto it(std::find_if(
     352           0 :               f_socket_events.begin()
     353           0 :             , f_socket_events.end()
     354           0 :             , [&evts](socket_evt::pointer_t evt)
     355           0 :             {
     356           0 :                 return evt->f_socket_events == evts;
     357           0 :             }));
     358           0 :     if(it != f_socket_events.end())
     359             :     {
     360           0 :         f_socket_events.erase(it);
     361             : 
     362           0 :         if(f_socket_events.empty())
     363             :         {
     364           0 :             communicator::instance()->remove_connection(g_socket_listener);
     365             : 
     366           0 :             g_socket_listener.reset();
     367             :         }
     368             :     }
     369           0 : }
     370             : 
     371             : 
     372             : /** \brief Check whether this socket_listener is a reader.
     373             :  *
     374             :  * A socket_listener is always a reader so this function always returns
     375             :  * true. When no messages are sent to us, the poll() doesn't return, so
     376             :  * it is safe to always mark this object as a writer.
     377             :  *
     378             :  * \return Always true.
     379             :  */
     380           0 : bool socket_listener::is_reader() const
     381             : {
     382           0 :     return true;
     383             : }
     384             : 
     385             : 
     386             : /** \brief Check whether this socket_listener is a writer.
     387             :  *
     388             :  * The socket listener is made a writer whenever listening for a "socket
     389             :  * open for connections" event. If no one is listening, then this function
     390             :  * returns false.
     391             :  *
     392             :  * \return true if there is at least one user listening for a socket to appear.
     393             :  */
     394           0 : bool socket_listener::is_writer() const
     395             : {
     396           0 :     cppthread::guard g(f_socket_mutex);
     397             : 
     398           0 :     for(auto it : f_socket_events)
     399             :     {
     400           0 :         if(!it->f_listening)
     401             :         {
     402           0 :             return true;
     403             :         }
     404             :     }
     405             : 
     406           0 :     return false;
     407             : }
     408             : 
     409             : 
     410             : /** \brief Get the NETLINK socket.
     411             :  *
     412             :  * This function return the socket connecting us to the NETLINK kernel
     413             :  * environment. It is always expected to be connected so it should never
     414             :  * return -1. If the connection fails (in the constructor), then the
     415             :  * socket_listener object doesn't get created and therefore this function
     416             :  * is not likely to ever return anything else than a valid socket descriptor.
     417             :  *
     418             :  * \return The NETLINK socket descriptor.
     419             :  */
     420           0 : int socket_listener::get_socket() const
     421             : {
     422           0 :     return f_netlink_socket.get();
     423             : }
     424             : 
     425             : 
     426             : /** \brief Process a timeout.
     427             :  *
     428             :  * This function is used to check whether we need the listener to be
     429             :  * enabled or not. If no one is listening for more socket status changes,
     430             :  * then it puts the socket listener in the \em disabled state.
     431             :  */
     432           0 : void socket_listener::process_timeout()
     433             : {
     434           0 :     cppthread::guard g(f_socket_mutex);
     435             : 
     436           0 :     for(auto it : f_socket_events)
     437             :     {
     438           0 :         if(!it->f_listening)
     439             :         {
     440           0 :             return;
     441             :         }
     442             :     }
     443             : 
     444             :     // nothing to check, go to sleep
     445             :     //
     446           0 :     set_enable(false);
     447             : }
     448             : 
     449             : 
     450             : /** \brief Process an incoming message.
     451             :  *
     452             :  * The NTLINK system sends packets to us. Each packet represents one message.
     453             :  * This function reads those messages one by one and processes them.
     454             :  *
     455             :  * The event of interest is SOCK_DIAG_BY_FAMILY. This includes an IP address
     456             :  * and a port which are checked against the IP address and port of each of
     457             :  * the socket_events object. If there is a match, then the
     458             :  * socket_events::process_listening() function gets called.
     459             :  *
     460             :  * The function returns once it receives the NLMSG_DONE message or the last
     461             :  * recvmsg() call returns 0, and of course on errors.
     462             :  */
     463           0 : void socket_listener::process_read()
     464             : {
     465           0 :     sockaddr_nl nladdr = {};
     466             : 
     467           0 :     nladdr.nl_family = AF_NETLINK;
     468             : 
     469           0 :     char buf[RECEIVE_BUFFER_SIZE * 2];
     470           0 :     struct iovec vec = {};
     471           0 :     vec.iov_base = buf;
     472           0 :     vec.iov_len = sizeof(buf);
     473             : 
     474             :     for(;;)
     475             :     {
     476           0 :         struct msghdr msg = {};
     477             : 
     478           0 :         msg.msg_name = &nladdr;
     479           0 :         msg.msg_namelen = sizeof(nladdr);
     480           0 :         msg.msg_iov = &vec;
     481           0 :         msg.msg_iovlen = 1;
     482             : 
     483           0 :         ssize_t size(recvmsg(f_netlink_socket.get(), &msg, 0));
     484           0 :         if(size < 0)
     485             :         {
     486           0 :             int const e(errno);
     487           0 :             SNAP_LOG_ERROR
     488           0 :                 << "recvmsg() returned with an error: "
     489             :                 << e
     490             :                 << " ("
     491           0 :                 << strerror(e)
     492             :                 << ")."
     493             :                 << SNAP_LOG_SEND;
     494           0 :             return;
     495             :         }
     496             : 
     497           0 :         if(size == 0)
     498             :         {
     499             :             // found end of message stream for now
     500             :             //
     501           0 :             return;
     502             :         }
     503             : 
     504           0 :         for(nlmsghdr * h(reinterpret_cast<nlmsghdr *>(buf));
     505           0 :             NLMSG_OK(h, size);
     506           0 :             h = NLMSG_NEXT(h, size))
     507             :         {
     508           0 :             switch(h->nlmsg_type)
     509             :             {
     510           0 :             case NLMSG_DONE:
     511           0 :                 return;
     512             : 
     513           0 :             case NLMSG_ERROR:
     514           0 :                 if(h->nlmsg_len < NLMSG_LENGTH(sizeof(nlmsgerr)))
     515             :                 {
     516           0 :                     SNAP_LOG_ERROR
     517           0 :                         << "unknown NLMSG_ERROR received (data buffer too small)."
     518             :                         << SNAP_LOG_SEND;
     519             :                 }
     520             :                 else
     521             :                 {
     522           0 :                     nlmsgerr const * err(reinterpret_cast<nlmsgerr const *>(NLMSG_DATA(h)));
     523           0 :                     int const e(-err->error);
     524           0 :                     if(e != ENOENT)
     525             :                     {
     526           0 :                         SNAP_LOG_ERROR
     527           0 :                             << "NETLINK error: "
     528             :                             << e
     529             :                             << " ("
     530           0 :                             << strerror(e)
     531             :                             << ")."
     532             :                             << SNAP_LOG_SEND;
     533             :                     }
     534             :                 }
     535           0 :                 break;
     536             : 
     537           0 :             case SOCK_DIAG_BY_FAMILY:
     538           0 :                 if(h->nlmsg_len < NLMSG_LENGTH(sizeof(inet_diag_msg)))
     539             :                 {
     540           0 :                     SNAP_LOG_WARNING
     541           0 :                         << "NETLINK length (h->nlmsg_len = "
     542           0 :                         << h->nlmsg_len
     543           0 :                         << ", expected at least "
     544           0 :                         << sizeof(inet_diag_msg)
     545             :                         << ") too small for a SOCK_DIAG_BY_FAMILY object."
     546             :                         << SNAP_LOG_SEND;
     547           0 :                     return;
     548             :                 }
     549             :                 else
     550             :                 {
     551           0 :                     inet_diag_msg const * diag(reinterpret_cast<inet_diag_msg const *>(NLMSG_DATA(h)));
     552           0 :                     if(diag->idiag_state == TCP_LISTEN_STATE)
     553             :                     {
     554             :                         // got a listen(), look for which connection this is
     555             :                         // and mark it as valid (open/listening)
     556             :                         //
     557           0 :                         for(auto it : f_socket_events)
     558             :                         {
     559           0 :                             if(!it->f_listening)
     560             :                             {
     561           0 :                                 addr::addr a(it->f_socket_events->get_addr());
     562           0 :                                 if(a.get_port() == diag->id.idiag_sport)
     563             :                                 {
     564           0 :                                     sockaddr_in in = {};
     565           0 :                                     a.get_ipv4(in);
     566           0 :                                     if(in.sin_addr.s_addr == diag->id.idiag_src[0])
     567             :                                     {
     568             :                                         // got it!
     569             :                                         //
     570           0 :                                         it->f_socket_events->process_listening();
     571           0 :                                         it->f_listening = true;
     572             : 
     573             :                                         // TBD: if we add two connections with the same IP:port combo,
     574             :                                         //      we get two separate socket_events but I do not know
     575             :                                         //      whether we'll get one or two replies... so at this
     576             :                                         //      time do not break this loop
     577             :                                         //break;
     578             :                                     }
     579             :                                 }
     580             :                             }
     581             :                         }
     582             :                     }
     583             :                 }
     584           0 :                 break;
     585             : 
     586           0 :             default:
     587           0 :                 SNAP_LOG_WARNING
     588           0 :                     << "unexpected message type (h->nlmsg_type) "
     589           0 :                     << h->nlmsg_type
     590             :                     << SNAP_LOG_SEND;
     591           0 :                 break;
     592             : 
     593             :             }
     594             :         }
     595           0 :     }
     596             : }
     597             : 
     598             : 
     599           0 : void socket_listener::process_write()
     600             : {
     601           0 :     cppthread::guard g(f_socket_mutex);
     602             : 
     603             :     // count the number of requests we have to send
     604             :     //
     605           0 :     int const count(std::count_if(
     606           0 :               f_socket_events.begin()
     607           0 :             , f_socket_events.end()
     608           0 :             , [](auto const & evt)
     609             :             {
     610           0 :                 return !evt->f_listening;
     611           0 :             }));
     612             : 
     613             :     struct nl_request
     614             :     {
     615             :         struct nlmsghdr         f_nlh;
     616             :         struct inet_diag_req_v2 f_inet;
     617             :     };
     618             : 
     619             :     // preallocation means that the pointers won't change
     620             :     // which is important here
     621             :     //
     622           0 :     std::vector<nl_request> req(count);
     623           0 :     std::vector<iovec> vec(count);
     624             : 
     625           0 :     int idx(0);
     626           0 :     for(auto it : f_socket_events)
     627             :     {
     628           0 :         if(!it->f_listening)
     629             :         {
     630           0 :             addr::addr const & a(it->f_socket_events->get_addr());
     631             : 
     632           0 :             sockaddr_in in = {};
     633           0 :             a.get_ipv4(in);
     634             : 
     635           0 :             req[idx].f_nlh.nlmsg_len = sizeof(nl_request);
     636           0 :             req[idx].f_nlh.nlmsg_type = SOCK_DIAG_BY_FAMILY;
     637           0 :             req[idx].f_nlh.nlmsg_flags = NLM_F_REQUEST;
     638           0 :             req[idx].f_inet.sdiag_family = AF_INET;
     639           0 :             req[idx].f_inet.sdiag_protocol = IPPROTO_TCP;
     640           0 :             req[idx].f_inet.idiag_ext = 0;
     641           0 :             req[idx].f_inet.pad = 0;
     642           0 :             req[idx].f_inet.idiag_states = 0;
     643           0 :             req[idx].f_inet.id.idiag_sport = in.sin_port;
     644           0 :             req[idx].f_inet.id.idiag_dport = 0;
     645           0 :             req[idx].f_inet.id.idiag_src[0] = in.sin_addr.s_addr;
     646           0 :             req[idx].f_inet.id.idiag_dst[0] = 0;
     647           0 :             req[idx].f_inet.id.idiag_if = 0;
     648           0 :             req[idx].f_inet.id.idiag_cookie[0] = INET_DIAG_NOCOOKIE;
     649           0 :             req[idx].f_inet.id.idiag_cookie[1] = INET_DIAG_NOCOOKIE;
     650             : 
     651           0 :             vec[idx].iov_base = &req[idx];
     652           0 :             vec[idx].iov_len = sizeof(nl_request);
     653             : 
     654           0 :             ++idx;
     655             :         }
     656             :     }
     657           0 :     if(idx != count)
     658             :     {
     659             :         throw event_dispatcher_implementation_error(
     660             :                   "somehow the number of requests counted ("
     661           0 :                 + std::to_string(count)
     662           0 :                 + ") did not match the number of requests created ("
     663           0 :                 + std::to_string(idx)
     664           0 :                 + ").");
     665             :     }
     666             : 
     667           0 :     sockaddr_nl nladdr = {};
     668             : 
     669           0 :     nladdr.nl_family = AF_NETLINK;
     670             : 
     671           0 :     msghdr msg = {};
     672             : 
     673           0 :     msg.msg_name = &nladdr;
     674           0 :     msg.msg_namelen = sizeof(nladdr);
     675           0 :     msg.msg_iov = vec.data();
     676           0 :     msg.msg_iovlen = count;
     677             : 
     678           0 :     int const r(sendmsg(f_netlink_socket.get(), &msg, 0));
     679           0 :     if(r < 0)
     680             :     {
     681           0 :         process_error();
     682           0 :         return;
     683             :     }
     684             : }
     685             : 
     686             : 
     687             : /** \brief Forward the error to the socket-events objects.
     688             :  *
     689             :  * This function forwards the error to all the socket-events objects
     690             :  * currently attached to the socket_listener object.
     691             :  */
     692           0 : void socket_listener::process_error()
     693             : {
     694           0 :     cppthread::guard g(f_socket_mutex);
     695             : 
     696           0 :     socket_evt::deque_t evts(f_socket_events);
     697           0 :     for(auto it : evts)
     698             :     {
     699           0 :         if(!it->f_listening)
     700             :         {
     701           0 :             it->f_socket_events->process_error();
     702             :         }
     703             :     }
     704           0 : }
     705             : 
     706             : 
     707             : /** \brief Forward the HUP signal to the socket-events objects.
     708             :  *
     709             :  * This function forwards the HUP to all the socket-events objects currently
     710             :  * attached to the socket_listener object.
     711             :  */
     712           0 : void socket_listener::process_hup()
     713             : {
     714           0 :     cppthread::guard g(f_socket_mutex);
     715             : 
     716           0 :     for(auto it : f_socket_events)
     717             :     {
     718           0 :         if(!it->f_listening)
     719             :         {
     720           0 :             it->f_socket_events->process_hup();
     721             :         }
     722             :     }
     723           0 : }
     724             : 
     725             : 
     726             : /** \brief Forward the invalid error to the socket-events objects.
     727             :  *
     728             :  * This function forwards the invalid error to all the socket-events objects
     729             :  * currently attached to the socket_listener object.
     730             :  */
     731           0 : void socket_listener::process_invalid()
     732             : {
     733           0 :     cppthread::guard g(f_socket_mutex);
     734             : 
     735           0 :     for(auto it : f_socket_events)
     736             :     {
     737           0 :         if(!it->f_listening)
     738             :         {
     739           0 :             it->f_socket_events->process_invalid();
     740             :         }
     741             :     }
     742           0 : }
     743             : 
     744             : 
     745             : 
     746             : 
     747             : 
     748             : 
     749             : }
     750             : // no name detail
     751             : 
     752             : 
     753             : 
     754             : /** \brief Initializes this socket event object.
     755             :  *
     756             :  * This function initializes the socket_events with the specified address.
     757             :  * The address will be used to listen for a `listen()` call from any process
     758             :  * on this system.
     759             :  *
     760             :  * \warning
     761             :  * This only works for local services. Services that run on a remote computer
     762             :  * must attempt to connect and fail on the connect until the remote service
     763             :  * is available.
     764             :  *
     765             :  * \param[in] a  The address and port to poll for a `listen()`.
     766             :  */
     767           0 : socket_events::socket_events(addr::addr const & a)
     768           0 :     : f_addr(a)
     769             : {
     770           0 :     socket_listener::instance()->add_socket_events(this);
     771           0 : }
     772             : 
     773             : 
     774             : /** \brief Initializes this socket events object.
     775             :  *
     776             :  * Initialize a socket_events object to listen to the specified address
     777             :  * and port for a new connection (i.e. for a process to call `listen()`
     778             :  * on that address:port combo).
     779             :  *
     780             :  * If this is the first socket_events created, then a new socket listener
     781             :  * is also created.
     782             :  *
     783             :  * Then this socket_events object gets added to that socket listener.
     784             :  *
     785             :  * \param[in] address  The address of the port to wait on.
     786             :  * \param[in] port  The port to poll for a `listen()`.
     787             :  */
     788           0 : socket_events::socket_events(
     789             :             std::string const & address
     790           0 :           , int port)
     791             :     : f_addr(addr::string_to_addr(
     792             :           address
     793             :         , "127.0.0.1"
     794             :         , port
     795           0 :         , "tcp"))     // we really only support TCP at the moment
     796             : {
     797           0 :     socket_listener::instance()->add_socket_events(this);
     798           0 : }
     799             : 
     800             : 
     801             : /** \brief Destroy instance.
     802             :  *
     803             :  * This function cleans up this socket event instance. This means the socket
     804             :  * address and port are removed from the actual socket listener and if that
     805             :  * was the last socket_events object, the socket listener is also destroyed.
     806             :  */
     807           0 : socket_events::~socket_events()
     808             : {
     809           0 :     socket_listener::instance()->remove_socket_events(this);
     810           0 : }
     811             : 
     812             : 
     813             : /** \brief This higher level connection has no socket.
     814             :  *
     815             :  * This function always returns -1 as there is no socket in this connection.
     816             :  *
     817             :  * \note
     818             :  * The one socket is found in the socket_listener which gets created with
     819             :  * the first socket_events (and destroyed with the last deleted
     820             :  * socket_events).
     821             :  *
     822             :  * \return Always -1.
     823             :  */
     824           0 : int socket_events::get_socket() const
     825             : {
     826           0 :     return -1;
     827             : }
     828             : 
     829             : 
     830             : /** \brief This function gives you access to the address of this connection.
     831             :  *
     832             :  * The loops used to create the NETLINK_SOCK_DIAG requests makes use of this
     833             :  * function to filter on this specific IP address and port.
     834             :  *
     835             :  * \return The address we are listening on.
     836             :  */
     837           0 : addr::addr const & socket_events::get_addr() const
     838             : {
     839           0 :     return f_addr;
     840             : }
     841             : 
     842             : 
     843             : /** \brief This function is called whenever you lose a connection.
     844             :  *
     845             :  * In most cases, you lose a connection because the service breaks (crashes
     846             :  * or was restarted) so you need to poll for a `listen()` again. This
     847             :  * function lets the socket_listener internal object know that you expect
     848             :  * a call to the process_listening() once the service is available again.
     849             :  */
     850           0 : void socket_events::lost_connection()
     851             : {
     852           0 :     socket_listener::instance()->lost_connection(this);
     853           0 : }
     854             : 
     855             : 
     856             : 
     857           6 : } // namespace ed
     858             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13