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

randombit / botan / 13843947991

13 Mar 2025 08:23PM UTC coverage: 91.649% (+0.006%) from 91.643%
13843947991

Pull #4772

github

web-flow
Merge fe5e2d619 into 328dbf707
Pull Request #4772: Consolidation and Improvement of Boost.Asio Socket Layer

95845 of 104578 relevant lines covered (91.65%)

11493429.43 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
            salen = static_cast<socklen_t>(res->ai_addrlen);
76
         }
77

78
         ::freeaddrinfo(res);
79

80
         if(m_socket == invalid_socket()) {
81
            throw System_Error(fmt("Connecting to {} for service {} failed with errno {}", hostname, service, errno),
82
                               errno);
83
         }
84
      }
85

86
      ~BSD_SocketUDP() override {
87
         close_socket(m_socket);
88
         m_socket = invalid_socket();
89
         socket_fini();
90
      }
91

92
      BSD_SocketUDP(const BSD_SocketUDP& other) = delete;
93
      BSD_SocketUDP(BSD_SocketUDP&& other) = delete;
94
      BSD_SocketUDP& operator=(const BSD_SocketUDP& other) = delete;
95
      BSD_SocketUDP& operator=(BSD_SocketUDP&& other) = delete;
96

97
      void write(const uint8_t buf[], size_t len) override {
98
         fd_set write_set;
99
         FD_ZERO(&write_set);
100
         FD_SET(m_socket, &write_set);
101

102
         size_t sent_so_far = 0;
103
         while(sent_so_far != len) {
104
            struct timeval timeout = make_timeout_tv();
105
            int active = ::select(static_cast<int>(m_socket + 1), nullptr, &write_set, nullptr, &timeout);
106

107
            if(active == 0) {
108
               throw System_Error("Timeout during socket write");
109
            }
110

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

126
      size_t read(uint8_t buf[], size_t len) override {
127
         fd_set read_set;
128
         FD_ZERO(&read_set);
129
         FD_SET(m_socket, &read_set);
130

131
         struct timeval timeout = make_timeout_tv();
132
         int active = ::select(static_cast<int>(m_socket + 1), &read_set, nullptr, nullptr, &timeout);
133

134
         if(active == 0) {
135
            throw System_Error("Timeout during socket read");
136
         }
137

138
         socket_op_ret_type got =
139
            ::recvfrom(m_socket, cast_uint8_ptr_to_char(buf), static_cast<sendrecv_len_type>(len), 0, nullptr, nullptr);
140

141
         if(got < 0) {
142
            throw System_Error("Socket read failed", errno);
143
         }
144

145
         return static_cast<size_t>(got);
146
      }
147

148
   private:
149
   #if defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
150
      typedef SOCKET socket_type;
151
      typedef int socket_op_ret_type;
152
      typedef int sendrecv_len_type;
153

154
      static socket_type invalid_socket() { return INVALID_SOCKET; }
155

156
      static void close_socket(socket_type s) { ::closesocket(s); }
157

158
      static std::string get_last_socket_error() { return std::to_string(::WSAGetLastError()); }
159

160
      static bool nonblocking_connect_in_progress() { return (::WSAGetLastError() == WSAEWOULDBLOCK); }
161

162
      static void set_nonblocking(socket_type s) {
163
         u_long nonblocking = 1;
164
         ::ioctlsocket(s, FIONBIO, &nonblocking);
165
      }
166

167
      static void socket_init() {
168
         WSAData wsa_data;
169
         WORD wsa_version = MAKEWORD(2, 2);
170

171
         if(::WSAStartup(wsa_version, &wsa_data) != 0) {
172
            throw System_Error("WSAStartup() failed", WSAGetLastError());
173
         }
174

175
         if(LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2) {
176
            ::WSACleanup();
177
            throw System_Error("Could not find a usable version of Winsock.dll");
178
         }
179
      }
180

181
      static void socket_fini() { ::WSACleanup(); }
182
   #else
183
      typedef int socket_type;
184
      typedef ssize_t socket_op_ret_type;
185
      typedef size_t sendrecv_len_type;
186

187
      static socket_type invalid_socket() { return -1; }
188

189
      static void close_socket(socket_type s) { ::close(s); }
190

191
      static std::string get_last_socket_error() { return ::strerror(errno); }
192

193
      static bool nonblocking_connect_in_progress() { return (errno == EINPROGRESS); }
194

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

201
      static void socket_init() {}
202

203
      static void socket_fini() {}
204
   #endif
205
      sockaddr_storage sa;
206
      socklen_t salen;
207

208
      struct timeval make_timeout_tv() const {
209
         struct timeval tv;
210
         tv.tv_sec = static_cast<decltype(timeval::tv_sec)>(m_timeout.count() / 1000000);
211
         tv.tv_usec = static_cast<decltype(timeval::tv_usec)>(m_timeout.count() % 1000000);
212
         return tv;
213
      }
214

215
      const std::chrono::microseconds m_timeout;
216
      socket_type m_socket;
217
};
218
#endif
219
}  // namespace
220

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

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

246
}  // 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