Repository navigation
Expand file tree
/
Copy pathSocketOptions.cpp
More file actions
272 lines (253 loc) · 11.1 KB
/
Copy pathSocketOptions.cpp
File metadata and controls
272 lines (253 loc) · 11.1 KB
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#ifdef __APPLE__
#include <libproc.h>
#include <unistd.h>
#endif
#endif
#include "prx/libkernel/Socket/include/SocketError.hpp"
#include "prx/libkernel/Socket/include/SocketOptions.hpp"
#include <algorithm>
#include <cstring>
namespace GuestSockets {
namespace {
constexpr int GuestAcceptConnection = 0x2;
constexpr int GuestReuseAddress = 0x4;
constexpr int GuestKeepAlive = 0x8;
constexpr int GuestDontRoute = 0x10;
constexpr int GuestBroadcast = 0x20;
constexpr int GuestLinger = 0x80;
constexpr int GuestOobInline = 0x100;
constexpr int GuestNoSigpipe = 0x800;
constexpr int GuestSendBuffer = 0x1001;
constexpr int GuestReceiveBuffer = 0x1002;
constexpr int GuestSendTimeout = 0x1005;
constexpr int GuestReceiveTimeout = 0x1006;
constexpr int GuestError = 0x1007;
constexpr int GuestType = 0x1008;
constexpr int GuestNonBlocking = 0x1200;
constexpr int GuestTcpNoDelay = 1;
constexpr int GuestTcpNoDelayFlag = 4;
constexpr int GuestIpv6Only = 27;
constexpr int ErrorInvalid = 22;
constexpr int ErrorDomain = 33;
constexpr int ErrorNoProtocolOption = 42;
constexpr int MaximumLinger = 65535;
constexpr std::int64_t LongestTimeout = 0x7fffffffffffffffLL;
struct GuestLingerValue {
std::int32_t enabled;
std::int32_t seconds;
};
struct GuestTimeval {
std::int64_t seconds;
std::int64_t microseconds;
};
std::int64_t TimeoutOf(const GuestTimeval& time) {
if (time.seconds > INT32_MAX) return LongestTimeout;
return (time.seconds << 32) + static_cast<std::int64_t>((static_cast<std::uint64_t>(time.microseconds) * ((std::uint64_t{1} << 63) / 500000)) >> 32);
}
GuestTimeval TimevalOf(std::int64_t timeout) {
return {timeout >> 32, (1000000 * static_cast<std::int64_t>(static_cast<std::uint32_t>(timeout))) >> 32};
}
#ifdef _WIN32
using NativeHandle = SOCKET;
using NativeOptionValue = const char*;
using NativeOptionOutput = char*;
int HostError() { return GuestErrorFromHost(WSAGetLastError()); }
#else
using NativeHandle = int;
using NativeOptionValue = const void*;
using NativeOptionOutput = void*;
int HostError() { return GuestErrorFromHost(errno); }
#endif
int NativeBoolean(int option) {
switch (option) {
case GuestReuseAddress: return SO_REUSEADDR;
case GuestKeepAlive: return SO_KEEPALIVE;
case GuestDontRoute: return SO_DONTROUTE;
case GuestBroadcast: return SO_BROADCAST;
case GuestOobInline: return SO_OOBINLINE;
default: return -1;
}
}
int Copy(void* output, std::uint32_t* length, const void* value, std::uint32_t size) {
const std::uint32_t count = std::min(*length, size);
if (count != 0 && !output) return ErrorInvalid;
if (count != 0) std::memcpy(output, value, count);
*length = count;
return 0;
}
}
int SetSocketLevelOption(std::uintptr_t native, SocketOptionState& state, int option, const void* value, std::uint32_t length) {
const auto handle = static_cast<NativeHandle>(native);
if (option == GuestLinger) {
if (length < sizeof(GuestLingerValue)) return ErrorInvalid;
if (!value) return ErrorInvalid;
GuestLingerValue guest;
std::memcpy(&guest, value, sizeof(guest));
if (guest.seconds < 0 || guest.seconds > MaximumLinger) return ErrorDomain;
#ifdef _WIN32
linger request{};
request.l_onoff = guest.enabled != 0 ? 1 : 0;
request.l_linger = static_cast<u_short>(guest.seconds);
#else
linger request{};
request.l_onoff = guest.enabled != 0 ? 1 : 0;
request.l_linger = guest.seconds;
#endif
if (::setsockopt(handle, SOL_SOCKET, SO_LINGER, reinterpret_cast<NativeOptionValue>(&request), sizeof(request))) return HostError();
return 0;
}
if (option == GuestSendTimeout || option == GuestReceiveTimeout) {
if (length < sizeof(GuestTimeval)) return ErrorInvalid;
if (!value) return ErrorInvalid;
GuestTimeval time;
std::memcpy(&time, value, sizeof(time));
if (time.seconds < 0 || time.microseconds < 0 || time.microseconds >= 1000000) return ErrorDomain;
(option == GuestSendTimeout ? state.sendTimeout : state.receiveTimeout) = TimeoutOf(time);
return 0;
}
if (option == GuestNoSigpipe || option == GuestNonBlocking) {
if (length < sizeof(int)) return ErrorInvalid;
if (!value) return ErrorInvalid;
int number;
std::memcpy(&number, value, sizeof(number));
if (option == GuestNoSigpipe) {
state.noSignal = number != 0;
return 0;
}
#ifdef _WIN32
unsigned long enabled = number != 0;
if (ioctlsocket(handle, FIONBIO, &enabled)) return HostError();
#else
int enabled = number != 0;
if (::ioctl(handle, FIONBIO, &enabled)) return HostError();
#endif
state.nonblocking = enabled != 0;
return 0;
}
const bool native_boolean = NativeBoolean(option) != -1;
if (native_boolean || option == GuestSendBuffer || option == GuestReceiveBuffer) {
if (length < sizeof(int)) return ErrorInvalid;
if (!value) return ErrorInvalid;
int number;
std::memcpy(&number, value, sizeof(number));
if (option == GuestSendBuffer || option == GuestReceiveBuffer) {
if (number < 1) return ErrorInvalid;
const int nativeOption = option == GuestSendBuffer ? SO_SNDBUF : SO_RCVBUF;
if (::setsockopt(handle, SOL_SOCKET, nativeOption, reinterpret_cast<NativeOptionValue>(&number), sizeof(number))) return HostError();
return 0;
}
const int enabled = number != 0 ? 1 : 0;
if (::setsockopt(handle, SOL_SOCKET, NativeBoolean(option), reinterpret_cast<NativeOptionValue>(&enabled), sizeof(enabled))) return HostError();
return 0;
}
return ErrorNoProtocolOption;
}
bool NativeListening(std::uintptr_t native, bool& listening) {
#ifdef __APPLE__
socket_fdinfo info{};
if (proc_pidfdinfo(getpid(), static_cast<int>(native), PROC_PIDFDSOCKETINFO, &info, sizeof(info)) != static_cast<int>(sizeof(info))) return false;
listening = (info.psi.soi_options & SO_ACCEPTCONN) != 0;
#else
int accepting = 0;
socklen_t size = sizeof(accepting);
if (::getsockopt(static_cast<NativeHandle>(native), SOL_SOCKET, SO_ACCEPTCONN, reinterpret_cast<NativeOptionOutput>(&accepting), &size)) return false;
listening = accepting != 0;
#endif
return true;
}
int GetSocketLevelOption(std::uintptr_t native, SocketOptionState& state, int type, int option, void* value, std::uint32_t* length) {
const auto handle = static_cast<NativeHandle>(native);
if (option == GuestLinger) {
linger current{};
socklen_t size = sizeof(current);
if (::getsockopt(handle, SOL_SOCKET, SO_LINGER, reinterpret_cast<NativeOptionOutput>(¤t), &size)) return HostError();
const GuestLingerValue guest{current.l_onoff != 0 ? GuestLinger : 0, static_cast<std::int32_t>(current.l_linger)};
return Copy(value, length, &guest, sizeof(guest));
}
if (option == GuestSendTimeout || option == GuestReceiveTimeout) {
const GuestTimeval time = TimevalOf(option == GuestSendTimeout ? state.sendTimeout : state.receiveTimeout);
return Copy(value, length, &time, sizeof(time));
}
int number = 0;
if (const int nativeOption = NativeBoolean(option); nativeOption != -1) {
int enabled = 0;
socklen_t size = sizeof(enabled);
if (::getsockopt(handle, SOL_SOCKET, nativeOption, reinterpret_cast<NativeOptionOutput>(&enabled), &size)) return HostError();
number = enabled != 0 ? option : 0;
} else if (option == GuestAcceptConnection) {
bool listening = false;
if (!NativeListening(native, listening)) return HostError();
number = listening ? GuestAcceptConnection : 0;
} else if (option == GuestType) {
number = type;
} else if (option == GuestNoSigpipe) {
number = state.noSignal ? GuestNoSigpipe : 0;
} else if (option == GuestNonBlocking) {
number = state.nonblocking ? 1 : 0;
} else if (option == GuestError) {
int pending = 0;
socklen_t size = sizeof(pending);
if (::getsockopt(handle, SOL_SOCKET, SO_ERROR, reinterpret_cast<NativeOptionOutput>(&pending), &size)) return HostError();
#ifdef _WIN32
if (pending != 0 && pending == state.reportedError) pending = 0;
else if (pending != 0) state.reportedError = pending;
#endif
number = pending == 0 ? 0 : GuestErrorFromHost(pending);
} else if (option == GuestSendBuffer || option == GuestReceiveBuffer) {
socklen_t size = sizeof(number);
if (::getsockopt(handle, SOL_SOCKET, option == GuestSendBuffer ? SO_SNDBUF : SO_RCVBUF, reinterpret_cast<NativeOptionOutput>(&number), &size)) return HostError();
} else {
return ErrorNoProtocolOption;
}
return Copy(value, length, &number, sizeof(number));
}
int SetTcpLevelOption(std::uintptr_t native, bool tcp, int option, const void* value, std::uint32_t length) {
if (!tcp) return ErrorInvalid;
if (option != GuestTcpNoDelay) return ErrorNoProtocolOption;
if (length < sizeof(int)) return ErrorInvalid;
if (!value) return ErrorInvalid;
int number;
std::memcpy(&number, value, sizeof(number));
const int enabled = number != 0 ? 1 : 0;
if (::setsockopt(static_cast<NativeHandle>(native), IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<NativeOptionValue>(&enabled), sizeof(enabled))) return HostError();
return 0;
}
int GetTcpLevelOption(std::uintptr_t native, bool tcp, int option, void* value, std::uint32_t* length) {
if (!tcp) return ErrorInvalid;
if (option != GuestTcpNoDelay) return ErrorNoProtocolOption;
int enabled = 0;
socklen_t size = sizeof(enabled);
if (::getsockopt(static_cast<NativeHandle>(native), IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<NativeOptionOutput>(&enabled), &size)) return HostError();
const int number = enabled != 0 ? GuestTcpNoDelayFlag : 0;
return Copy(value, length, &number, sizeof(number));
}
int SetIpv6LevelOption(std::uintptr_t native, bool ipv6, int option, const void* value, std::uint32_t length) {
if (!ipv6) return ErrorInvalid;
if (option != GuestIpv6Only) return ErrorNoProtocolOption;
if (length != sizeof(int)) return ErrorInvalid;
if (!value) return ErrorInvalid;
int number;
std::memcpy(&number, value, sizeof(number));
const int enabled = number != 0 ? 1 : 0;
if (::setsockopt(static_cast<NativeHandle>(native), IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<NativeOptionValue>(&enabled), sizeof(enabled))) return HostError();
return 0;
}
int GetIpv6LevelOption(std::uintptr_t native, bool ipv6, int option, void* value, std::uint32_t* length) {
if (!ipv6) return ErrorInvalid;
if (option != GuestIpv6Only) return ErrorNoProtocolOption;
int enabled = 0;
socklen_t size = sizeof(enabled);
if (::getsockopt(static_cast<NativeHandle>(native), IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<NativeOptionOutput>(&enabled), &size)) return HostError();
const int number = enabled != 0 ? 1 : 0;
return Copy(value, length, &number, sizeof(number));
}
}