1 /*
2  *  Copyright (C) 2019 Savoir-faire Linux Inc.
3  *  Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program. If not, see <https://www.gnu.org/licenses/>.
17  */
18 #pragma once
19 
20 #include "def.h"
21 
22 #include "sockaddr.h"
23 #include "utils.h"
24 #include "log_enable.h"
25 
26 #ifdef _WIN32
27 #include <ws2tcpip.h>
28 #include <winsock2.h>
29 #else
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <unistd.h>
33 #endif
34 
35 #include <functional>
36 #include <thread>
37 #include <atomic>
38 #include <iostream>
39 
40 namespace dht {
41 namespace net {
42 
43 static const constexpr in_port_t DHT_DEFAULT_PORT = 4222;
44 
45 int bindSocket(const SockAddr& addr, SockAddr& bound);
46 
47 bool setNonblocking(int fd, bool nonblocking = true);
48 
49 #ifdef _WIN32
50 void udpPipe(int fds[2]);
51 #endif
52 struct ReceivedPacket {
53     Blob data;
54     SockAddr from;
55     time_point received;
56 };
57 
58 class OPENDHT_PUBLIC DatagramSocket {
59 public:
60     using OnReceive = std::function<void(std::unique_ptr<ReceivedPacket>&& packet)>;
~DatagramSocket()61     virtual ~DatagramSocket() {};
62 
63     virtual int sendTo(const SockAddr& dest, const uint8_t* data, size_t size, bool replied) = 0;
64 
setOnReceive(OnReceive && cb)65     inline void setOnReceive(OnReceive&& cb) {
66         rx_callback = std::move(cb);
67     }
68 
69     virtual const SockAddr& getBound(sa_family_t family = AF_UNSPEC) const = 0;
70     virtual bool hasIPv4() const = 0;
71     virtual bool hasIPv6() const = 0;
72 
73     in_port_t getPort(sa_family_t family = AF_UNSPEC) const {
74         return getBound(family).getPort();
75     }
76 
77     virtual void stop() = 0;
78 protected:
79 
onReceived(std::unique_ptr<ReceivedPacket> && packet)80     inline void onReceived(std::unique_ptr<ReceivedPacket>&& packet) {
81         if (rx_callback)
82             rx_callback(std::move(packet));
83     }
84 private:
85     OnReceive rx_callback;
86 };
87 
88 class OPENDHT_PUBLIC UdpSocket : public DatagramSocket {
89 public:
90     UdpSocket(in_port_t port, const Logger& l = {});
91     UdpSocket(const SockAddr& bind4, const SockAddr& bind6, const Logger& l = {});
92     ~UdpSocket();
93 
94     int sendTo(const SockAddr& dest, const uint8_t* data, size_t size, bool replied) override;
95 
96     const SockAddr& getBound(sa_family_t family = AF_UNSPEC) const override {
97         return (family == AF_INET6) ? bound6 : bound4;
98     }
99 
hasIPv4()100     bool hasIPv4() const override { return s4 != -1; }
hasIPv6()101     bool hasIPv6() const override { return s6 != -1; }
102 
103     void stop() override;
104 private:
105     Logger logger;
106     int s4 {-1};
107     int s6 {-1};
108     int stopfd {-1};
109     SockAddr bound4, bound6;
110     std::thread rcv_thread {};
111     std::atomic_bool running {false};
112 
113     void openSockets(const SockAddr& bind4, const SockAddr& bind6);
114 };
115 
116 }
117 }
118