Skip to content

Commit fe5e2d6

Browse files
committed
Cleanup in socket/asio code
1 parent 97018f5 commit fe5e2d6

File tree

4 files changed

+161
-203
lines changed

4 files changed

+161
-203
lines changed

src/lib/utils/socket/info.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ name -> "Socket"
1010
uri.h
1111
socket.h
1212
socket_udp.h
13+
socket_asio.h
1314
</header:internal>
1415

1516
<libs>

src/lib/utils/socket/socket.cpp

Lines changed: 3 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,11 @@
1010
#include <botan/exceptn.h>
1111
#include <botan/mem_ops.h>
1212
#include <botan/internal/fmt.h>
13+
#include <botan/internal/socket_asio.h>
1314
#include <botan/internal/target_info.h>
1415
#include <chrono>
1516

16-
#if defined(BOTAN_HAS_BOOST_ASIO)
17-
/*
18-
* We don't need serial port support anyway, and asking for it causes
19-
* macro conflicts with termios.h when this file is included in the
20-
* amalgamation.
21-
*/
22-
#define BOOST_ASIO_DISABLE_SERIAL_PORT
23-
#include <boost/asio.hpp>
24-
#include <boost/asio/system_timer.hpp>
25-
26-
#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS)
17+
#if defined(BOTAN_TARGET_OS_HAS_SOCKETS)
2718
#include <errno.h>
2819
#include <fcntl.h>
2920
#include <netdb.h>
@@ -41,97 +32,7 @@ namespace Botan {
4132

4233
namespace {
4334

44-
#if defined(BOTAN_HAS_BOOST_ASIO)
45-
46-
class Asio_Socket final : public OS::Socket {
47-
public:
48-
Asio_Socket(std::string_view hostname, std::string_view service, std::chrono::milliseconds timeout) :
49-
m_timeout(timeout), m_timer(m_io), m_tcp(m_io) {
50-
m_timer.expires_after(m_timeout);
51-
check_timeout();
52-
53-
boost::asio::ip::tcp::resolver resolver(m_io);
54-
boost::asio::ip::tcp::resolver::results_type dns_iter =
55-
resolver.resolve(std::string{hostname}, std::string{service});
56-
57-
boost::system::error_code ec = boost::asio::error::would_block;
58-
59-
auto connect_cb = [&ec](const boost::system::error_code& e, const auto&) { ec = e; };
60-
61-
boost::asio::async_connect(m_tcp, dns_iter.begin(), dns_iter.end(), connect_cb);
62-
63-
while(ec == boost::asio::error::would_block) {
64-
m_io.run_one();
65-
}
66-
67-
if(ec) {
68-
throw boost::system::system_error(ec);
69-
}
70-
if(m_tcp.is_open() == false) {
71-
throw System_Error(fmt("Connection to host {} failed", hostname));
72-
}
73-
}
74-
75-
void write(const uint8_t buf[], size_t len) override {
76-
m_timer.expires_after(m_timeout);
77-
78-
boost::system::error_code ec = boost::asio::error::would_block;
79-
80-
m_tcp.async_send(boost::asio::buffer(buf, len), [&ec](boost::system::error_code e, size_t) { ec = e; });
81-
82-
while(ec == boost::asio::error::would_block) {
83-
m_io.run_one();
84-
}
85-
86-
if(ec) {
87-
throw boost::system::system_error(ec);
88-
}
89-
}
90-
91-
size_t read(uint8_t buf[], size_t len) override {
92-
m_timer.expires_after(m_timeout);
93-
94-
boost::system::error_code ec = boost::asio::error::would_block;
95-
size_t got = 0;
96-
97-
m_tcp.async_read_some(boost::asio::buffer(buf, len), [&](boost::system::error_code cb_ec, size_t cb_got) {
98-
ec = cb_ec;
99-
got = cb_got;
100-
});
101-
102-
while(ec == boost::asio::error::would_block) {
103-
m_io.run_one();
104-
}
105-
106-
if(ec) {
107-
if(ec == boost::asio::error::eof) {
108-
return 0;
109-
}
110-
throw boost::system::system_error(ec); // Some other error.
111-
}
112-
113-
return got;
114-
}
115-
116-
private:
117-
void check_timeout() {
118-
if(m_tcp.is_open() && m_timer.expiry() < std::chrono::system_clock::now()) {
119-
boost::system::error_code err;
120-
121-
// NOLINTNEXTLINE(bugprone-unused-return-value,cert-err33-c)
122-
m_tcp.close(err);
123-
}
124-
125-
m_timer.async_wait(std::bind(&Asio_Socket::check_timeout, this));
126-
}
127-
128-
const std::chrono::milliseconds m_timeout;
129-
boost::asio::io_context m_io;
130-
boost::asio::system_timer m_timer;
131-
boost::asio::ip::tcp::socket m_tcp;
132-
};
133-
134-
#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
35+
#if defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
13536

13637
class BSD_Socket final : public OS::Socket {
13738
private:

src/lib/utils/socket/socket_asio.h

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
* (C) 2015,2016,2017 Jack Lloyd
3+
* (C) 2016 Daniel Neus
4+
* (C) 2019 Nuno Goncalves <[email protected]>
5+
* (C) 2025 Kagan Can Sit
6+
*
7+
* Botan is released under the Simplified BSD License (see license.txt)
8+
*/
9+
10+
#ifndef BOTAN_SOCKET_ASIO_H_
11+
#define BOTAN_SOCKET_ASIO_H_
12+
13+
#include <botan/exceptn.h>
14+
#include <botan/internal/fmt.h>
15+
#include <botan/internal/socket.h>
16+
#include <botan/internal/socket_udp.h>
17+
#include <chrono>
18+
19+
#if defined(BOTAN_HAS_BOOST_ASIO)
20+
/*
21+
* We don't need serial port support anyway, and asking for it causes
22+
* macro conflicts with termios.h when this file is included in the
23+
* amalgamation.
24+
*/
25+
#define BOOST_ASIO_DISABLE_SERIAL_PORT
26+
#include <boost/asio.hpp>
27+
#include <boost/asio/system_timer.hpp>
28+
#endif
29+
30+
namespace Botan::OS {
31+
32+
#if defined(BOTAN_HAS_BOOST_ASIO)
33+
34+
/*
35+
* A template-based implementation of Asio sockets that can be used
36+
* for both TCP and UDP protocols, reducing code duplication.
37+
*
38+
* @param BaseSocketType The base socket class (Socket for TCP, SocketUDP for UDP)
39+
* @param ProtocolType The Asio protocol type (boost::asio::ip::tcp or boost::asio::ip::udp)
40+
*/
41+
template <typename BaseSocketType, typename ProtocolType>
42+
class Asio_Socket_Base : public BaseSocketType {
43+
public:
44+
Asio_Socket_Base(std::string_view hostname, std::string_view service, std::chrono::milliseconds timeout) :
45+
// Convert milliseconds to microseconds for consistent timing operations
46+
m_timeout(std::chrono::duration_cast<std::chrono::microseconds>(timeout)), m_timer(m_io), m_socket(m_io) {
47+
m_timer.expires_after(m_timeout);
48+
check_timeout();
49+
50+
// Resolve the DNS
51+
typename ProtocolType::resolver resolver(m_io);
52+
auto endPoints = resolver.resolve(std::string{hostname}, std::string{service});
53+
54+
boost::system::error_code ec = boost::asio::error::would_block;
55+
56+
auto connect_cb = [&ec](const boost::system::error_code& e, const typename ProtocolType::endpoint&) {
57+
ec = e;
58+
};
59+
60+
boost::asio::async_connect(m_socket, endPoints, connect_cb);
61+
62+
while(ec == boost::asio::error::would_block) {
63+
m_io.run_one();
64+
}
65+
66+
if(ec) {
67+
throw boost::system::system_error(ec);
68+
}
69+
70+
if(m_socket.is_open() == false) {
71+
throw System_Error(fmt("Connection to host {} failed", hostname));
72+
}
73+
}
74+
75+
void write(const uint8_t buf[], size_t len) override {
76+
m_timer.expires_after(m_timeout);
77+
78+
boost::system::error_code ec = boost::asio::error::would_block;
79+
80+
m_socket.async_send(boost::asio::buffer(buf, len), [&ec](boost::system::error_code e, size_t) { ec = e; });
81+
82+
while(ec == boost::asio::error::would_block) {
83+
m_io.run_one();
84+
}
85+
86+
if(ec) {
87+
throw boost::system::system_error(ec);
88+
}
89+
}
90+
91+
size_t read(uint8_t buf[], size_t len) override {
92+
m_timer.expires_after(m_timeout);
93+
94+
boost::system::error_code ec = boost::asio::error::would_block;
95+
size_t got = 0;
96+
97+
// Use different read methods based on protocol type
98+
if constexpr(std::is_same_v<ProtocolType, boost::asio::ip::tcp>) {
99+
m_socket.async_read_some(boost::asio::buffer(buf, len),
100+
[&](boost::system::error_code cb_ec, size_t cb_got) {
101+
ec = cb_ec;
102+
got = cb_got;
103+
});
104+
} else if constexpr(std::is_same_v<ProtocolType, boost::asio::ip::udp>) {
105+
m_socket.async_receive(boost::asio::buffer(buf, len), [&](boost::system::error_code cb_ec, size_t cb_got) {
106+
ec = cb_ec;
107+
got = cb_got;
108+
});
109+
}
110+
111+
while(ec == boost::asio::error::would_block) {
112+
m_io.run_one();
113+
}
114+
115+
if(ec) {
116+
if(ec == boost::asio::error::eof) {
117+
return 0;
118+
}
119+
throw boost::system::system_error(ec); // Some other error.
120+
}
121+
122+
return got;
123+
}
124+
125+
private:
126+
void check_timeout() {
127+
if(m_socket.is_open() && m_timer.expiry() < std::chrono::system_clock::now()) {
128+
boost::system::error_code err;
129+
130+
// NOLINTNEXTLINE(bugprone-unused-return-value,cert-err33-c)
131+
m_socket.close(err);
132+
}
133+
134+
m_timer.async_wait(std::bind(&Asio_Socket_Base::check_timeout, this));
135+
}
136+
137+
const std::chrono::microseconds m_timeout;
138+
boost::asio::io_context m_io;
139+
boost::asio::system_timer m_timer;
140+
typename ProtocolType::socket m_socket;
141+
};
142+
143+
// Convenience type aliases for common socket types
144+
using Asio_Socket = Asio_Socket_Base<Socket, boost::asio::ip::tcp>;
145+
using Asio_SocketUDP = Asio_Socket_Base<SocketUDP, boost::asio::ip::udp>;
146+
147+
#endif // BOTAN_HAS_BOOST_ASIO
148+
149+
} // namespace Botan::OS
150+
151+
#endif // BOTAN_SOCKET_ASIO_H_

0 commit comments

Comments
 (0)