• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 14105686873

27 Mar 2025 11:41AM UTC coverage: 91.539% (+0.009%) from 91.53%
14105686873

Pull #4772

github

web-flow
Merge de813365d into 64addbb98
Pull Request #4772: Consolidation and Improvement of Boost.Asio Socket Layer

95331 of 104142 relevant lines covered (91.54%)

11508679.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.89
/src/lib/utils/socket/socket_udp.cpp
1
/*
2
* (C) 2015,2016,2017 Jack Lloyd
3
* (C) 2016 Daniel Neus
4
* (C) 2019 Nuno Goncalves <nunojpg@gmail.com>
5
*
6
* Botan is released under the Simplified BSD License (see license.txt)
7
*/
8

9
#include <botan/internal/socket_udp.h>
10

11
#include <botan/exceptn.h>
12
#include <botan/mem_ops.h>
13
#include <botan/internal/fmt.h>
14
#include <botan/internal/socket_asio.h>
15
#include <botan/internal/target_info.h>
16
#include <botan/internal/uri.h>
17
#include <chrono>
18

19
#if defined(BOTAN_TARGET_OS_HAS_SOCKETS)
20
   #include <errno.h>
21
   #include <fcntl.h>
22
   #include <netdb.h>
23
   #include <netinet/in.h>
24
   #include <string.h>
25
   #include <sys/socket.h>
26
   #include <sys/time.h>
27
   #include <unistd.h>
28

29
#elif defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
30
   #include <ws2tcpip.h>
31
#endif
32

33
namespace Botan {
34

35
namespace {
36

37
#if defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
38
class BSD_SocketUDP final : public OS::SocketUDP {
39
   public:
40
      BSD_SocketUDP(std::string_view hostname, std::string_view service, std::chrono::microseconds timeout) :
41
            m_timeout(timeout) {
42
         socket_init();
43

44
         m_socket = invalid_socket();
45

46
         addrinfo* res;
47
         addrinfo hints;
48
         clear_mem(&hints, 1);
49
         hints.ai_family = AF_UNSPEC;
50
         hints.ai_socktype = SOCK_DGRAM;
51

52
         const std::string hostname_str(hostname);
53
         const std::string service_str(service);
54

55
         int rc = ::getaddrinfo(hostname_str.c_str(), service_str.c_str(), &hints, &res);
56

57
         if(rc != 0) {
58
            throw System_Error(fmt("Name resolution failed for {}", hostname), rc);
59
         }
60

61
         for(addrinfo* rp = res; (m_socket == invalid_socket()) && (rp != nullptr); rp = rp->ai_next) {
62
            if(rp->ai_family != AF_INET && rp->ai_family != AF_INET6) {
63
               continue;
64
            }
65

66
            m_socket = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
67

68
            if(m_socket == invalid_socket()) [[unlikely]] {
69
               // unsupported socket type?
70
               continue;
71
            }
72

73
            set_nonblocking(m_socket);
74
            memcpy(&sa, res->ai_addr, res->ai_addrlen);
75
   #if defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
76
            salen = static_cast<socklen_t>(res->ai_addrlen);
77
   #else
78
            salen = res->ai_addrlen;
79
   #endif
80
         }
81

82
         ::freeaddrinfo(res);
83

84
         if(m_socket == invalid_socket()) {
85
            throw System_Error(fmt("Connecting to {} for service {} failed with errno {}", hostname, service, errno),
86
                               errno);
87
         }
88
      }
89

90
      ~BSD_SocketUDP() override {
91
         close_socket(m_socket);
92
         m_socket = invalid_socket();
93
         socket_fini();
94
      }
95

96
      BSD_SocketUDP(const BSD_SocketUDP& other) = delete;
97
      BSD_SocketUDP(BSD_SocketUDP&& other) = delete;
98
      BSD_SocketUDP& operator=(const BSD_SocketUDP& other) = delete;
99
      BSD_SocketUDP& operator=(BSD_SocketUDP&& other) = delete;
100

101
      void write(const uint8_t buf[], size_t len) override {
102
         fd_set write_set;
103
         FD_ZERO(&write_set);
104
         FD_SET(m_socket, &write_set);
105

106
         size_t sent_so_far = 0;
107
         while(sent_so_far != len) {
108
            struct timeval timeout = make_timeout_tv();
109
            int active = ::select(static_cast<int>(m_socket + 1), nullptr, &write_set, nullptr, &timeout);
110

111
            if(active == 0) {
112
               throw System_Error("Timeout during socket write");
113
            }
114

115
            const size_t left = len - sent_so_far;
116
            socket_op_ret_type sent = ::sendto(m_socket,
117
                                               cast_uint8_ptr_to_char(buf + sent_so_far),
118
                                               static_cast<sendrecv_len_type>(left),
119
                                               0,
120
                                               reinterpret_cast<sockaddr*>(&sa),
121
                                               salen);
122
            if(sent < 0) {
123
               throw System_Error("Socket write failed", errno);
124
            } else {
125
               sent_so_far += static_cast<size_t>(sent);
126
            }
127
         }
128
      }
129

130
      size_t read(uint8_t buf[], size_t len) override {
131
         fd_set read_set;
132
         FD_ZERO(&read_set);
133
         FD_SET(m_socket, &read_set);
134

135
         struct timeval timeout = make_timeout_tv();
136
         int active = ::select(static_cast<int>(m_socket + 1), &read_set, nullptr, nullptr, &timeout);
137

138
         if(active == 0) {
139
            throw System_Error("Timeout during socket read");
140
         }
141

142
         socket_op_ret_type got =
143
            ::recvfrom(m_socket, cast_uint8_ptr_to_char(buf), static_cast<sendrecv_len_type>(len), 0, nullptr, nullptr);
144

145
         if(got < 0) {
146
            throw System_Error("Socket read failed", errno);
147
         }
148

149
         return static_cast<size_t>(got);
150
      }
151

152
   private:
153
   #if defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
154
      typedef SOCKET socket_type;
155
      typedef int socket_op_ret_type;
156
      typedef int sendrecv_len_type;
157

158
      static socket_type invalid_socket() { return INVALID_SOCKET; }
159

160
      static void close_socket(socket_type s) { ::closesocket(s); }
161

162
      static std::string get_last_socket_error() { return std::to_string(::WSAGetLastError()); }
163

164
      static bool nonblocking_connect_in_progress() { return (::WSAGetLastError() == WSAEWOULDBLOCK); }
165

166
      static void set_nonblocking(socket_type s) {
167
         u_long nonblocking = 1;
168
         ::ioctlsocket(s, FIONBIO, &nonblocking);
169
      }
170

171
      static void socket_init() {
172
         WSAData wsa_data;
173
         WORD wsa_version = MAKEWORD(2, 2);
174

175
         if(::WSAStartup(wsa_version, &wsa_data) != 0) {
176
            throw System_Error("WSAStartup() failed", WSAGetLastError());
177
         }
178

179
         if(LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2) {
180
            ::WSACleanup();
181
            throw System_Error("Could not find a usable version of Winsock.dll");
182
         }
183
      }
184

185
      static void socket_fini() { ::WSACleanup(); }
186
   #else
187
      typedef int socket_type;
188
      typedef ssize_t socket_op_ret_type;
189
      typedef size_t sendrecv_len_type;
190

191
      static socket_type invalid_socket() { return -1; }
192

193
      static void close_socket(socket_type s) { ::close(s); }
194

195
      static std::string get_last_socket_error() { return ::strerror(errno); }
196

197
      static bool nonblocking_connect_in_progress() { return (errno == EINPROGRESS); }
198

199
      static void set_nonblocking(socket_type s) {
200
         if(::fcntl(s, F_SETFL, O_NONBLOCK) < 0) {
201
            throw System_Error("Setting socket to non-blocking state failed", errno);
202
         }
203
      }
204

205
      static void socket_init() {}
206

207
      static void socket_fini() {}
208
   #endif
209
      sockaddr_storage sa;
210
      socklen_t salen;
211

212
      struct timeval make_timeout_tv() const {
213
         struct timeval tv;
214
         tv.tv_sec = static_cast<decltype(timeval::tv_sec)>(m_timeout.count() / 1000000);
215
         tv.tv_usec = static_cast<decltype(timeval::tv_usec)>(m_timeout.count() % 1000000);
216
         return tv;
217
      }
218

219
      const std::chrono::microseconds m_timeout;
220
      socket_type m_socket;
221
};
222
#endif
223
}  // namespace
224

225
std::unique_ptr<OS::SocketUDP> OS::open_socket_udp(std::string_view hostname,
6✔
226
                                                   std::string_view service,
227
                                                   std::chrono::microseconds timeout) {
228
#if defined(BOTAN_HAS_BOOST_ASIO)
229
   // In the old implementation, the parameter taken for UDP was microseconds. Homewer template calls are in milliseconds.
230
   return std::make_unique<Asio_SocketUDP>(
6✔
231
      hostname, service, std::chrono::duration_cast<std::chrono::milliseconds>(timeout));
6✔
232
#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
233
   return std::make_unique<BSD_SocketUDP>(hostname, service, timeout);
234
#else
235
   BOTAN_UNUSED(hostname);
236
   BOTAN_UNUSED(service);
237
   BOTAN_UNUSED(timeout);
238
   return std::unique_ptr<OS::SocketUDP>();
239
#endif
240
}
241

242
std::unique_ptr<OS::SocketUDP> OS::open_socket_udp(std::string_view uri_string, std::chrono::microseconds timeout) {
6✔
243
   const auto uri = URI::from_any(uri_string);
6✔
244
   if(uri.port() == 0) {
6✔
245
      throw Invalid_Argument("UDP port not specified");
×
246
   }
247
   return open_socket_udp(uri.host(), std::to_string(uri.port()), timeout);
12✔
248
}
6✔
249

250
}  // namespace Botan
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc