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 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
45 : //
46 : #include <snaplogger/message.h>
47 :
48 :
49 : // snapdev
50 : //
51 : #include <snapdev/raii_generic_deleter.h>
52 :
53 :
54 : // cppthread
55 : //
56 : #include <cppthread/guard.h>
57 : #include <cppthread/mutex.h>
58 :
59 :
60 : // libaddr
61 : //
62 : #include <libaddr/addr_parser.h>
63 :
64 :
65 : // C++
66 : //
67 : #include <algorithm>
68 : #include <deque>
69 :
70 :
71 : // C
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 : 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 : snapdev::raii_fd_t f_netlink_socket = snapdev::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 : 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 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 0 : , f_socket_mutex(socket_mutex)
186 0 : , 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 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 : << "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 : << "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 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 0 : }
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 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 : {
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 : {
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 0 : }
405 :
406 0 : return false;
407 0 : }
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 0 : }
443 :
444 : // nothing to check, go to sleep
445 : //
446 0 : set_enable(false);
447 0 : }
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 : << "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 0 : }
581 0 : }
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 0 : }
657 0 : if(idx != count)
658 : {
659 0 : throw 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 0 : }
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 0 : }
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 0 : }
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 0 : }
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] address The address and port to poll for a `listen()`.
766 : */
767 0 : socket_events::socket_events(addr::addr const & address)
768 0 : : f_addr(address)
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 0 : : 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 : } // namespace ed
858 : // vim: ts=4 sw=4 et
|