1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
#include "net.hh"
namespace shared {
namespace net {
// Some calls use errno, some use strerr.
std::string get_net_error(const int code) noexcept {
return make_string_lower(std::string(gai_strerror(code)));
}
std::string get_errno_error() noexcept {
return make_string_lower(std::string(strerror(errno)));
}
std::shared_ptr<addrinfo> get_addr_info(const std::string_view address,
const std::string_view port,
const addrinfo* const hints) {
addrinfo* info;
if (int status = getaddrinfo(address.data(), port.data(), hints, &info)) {
throw std::runtime_error(get_net_error(status));
}
return std::shared_ptr<addrinfo>{info, [](auto p) { freeaddrinfo(p); }};
}
int make_socket(const addrinfo* const info) {
int sock = socket(info->ai_family, info->ai_socktype, info->ai_protocol);
if (sock == -1) {
throw std::runtime_error(get_errno_error());
}
int enable = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int));
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
return sock;
}
void bind_socket(const int sock, const addrinfo* const info) {
// Bind to the first we can, otherwise throw.
for (const addrinfo* i = info; i; i = i->ai_next) {
if (bind(sock, info->ai_addr, info->ai_addrlen) != -1) {
return;
}
}
throw std::runtime_error(get_errno_error());
}
void connect_socket(const int sock, const addrinfo* const info) {
if (connect(sock, info->ai_addr, info->ai_addrlen) == -1) {
throw std::runtime_error(get_errno_error());
}
}
void nonblock_socket(const int sock) {
if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
throw std::runtime_error(get_errno_error());
}
}
void* get_info_address(sockaddr& info) noexcept {
if (info.sa_family == AF_INET) {
return &(reinterpret_cast<sockaddr_in*>(&info)->sin_addr);
}
return &(reinterpret_cast<sockaddr_in6*>(&info)->sin6_addr);
}
void listen_socket(const int sock) {
fcntl(sock, F_SETFL, O_NONBLOCK);
if (listen(sock, SOMAXCONN) == -1) {
throw std::runtime_error(get_errno_error());
}
}
// Returns std::nullopt on failure! We lose the ability to throw here.
std::optional<accept_ret> get_accept(const int sock) {
accept_ret info;
socklen_t size = sizeof(info.storage);
info.socket =
accept(sock, reinterpret_cast<sockaddr*>(&info.storage), &size);
if (info.socket == -1) {
return std::nullopt;
}
return info;
}
void close_socket(const int sock) {
if (close(sock) == -1) {
throw std::runtime_error(get_errno_error());
}
}
// Returns the size of the receive buffer.
// For reliable, returns size of the stream (thanks [][][]).
// For unreliable, returns size of the next datagram available for reading.
std::size_t get_backlog_size(const int sock) {
size_t result = 0;
if (ioctl(sock, FIONREAD, &result) == -1) {
throw std::runtime_error(get_errno_error());
}
return result;
}
std::string get_socket_host_address(const int sock) {
sockaddr_in sin;
socklen_t size = sizeof(sin);
if (getsockname(sock, reinterpret_cast<sockaddr*>(&sin), &size) == -1) {
throw std::runtime_error(get_errno_error());
}
return std::string(inet_ntoa(sin.sin_addr));
}
std::string get_socket_host_port(const int sock) {
sockaddr_in sin;
socklen_t size = sizeof(sin);
if (getsockname(sock, reinterpret_cast<sockaddr*>(&sin), &size) == -1) {
throw std::runtime_error(get_errno_error());
}
return std::to_string(ntohs(sin.sin_port));
}
std::string get_socket_peer_address(const int sock) {
sockaddr_in sin;
socklen_t size = sizeof(sin);
if (getpeername(sock, reinterpret_cast<sockaddr*>(&sin), &size) == -1) {
throw std::runtime_error(get_errno_error());
}
return std::string(inet_ntoa(sin.sin_addr));
}
std::string get_socket_peer_port(const int sock) {
sockaddr_in sin;
socklen_t size = sizeof(sin);
if (getpeername(sock, reinterpret_cast<sockaddr*>(&sin), &size) == -1) {
throw std::runtime_error(get_errno_error());
}
return std::to_string(ntohs(sin.sin_port));
}
} // namespace net
} // namespace shared
|