diff options
| author | 2020-07-21 16:05:47 +1000 | |
|---|---|---|
| committer | 2020-07-21 16:05:47 +1000 | |
| commit | e9bfe05e04f4a83cadb2cc036689921defb4e342 (patch) | |
| tree | 12910935b977f97b94a3af6ae3210c097940b407 /src | |
| parent | Merge pull request #4324 from ReinUsesLisp/formats (diff) | |
| parent | core/network: Add network abstraction (diff) | |
| download | yuzu-e9bfe05e04f4a83cadb2cc036689921defb4e342.tar.gz yuzu-e9bfe05e04f4a83cadb2cc036689921defb4e342.tar.xz yuzu-e9bfe05e04f4a83cadb2cc036689921defb4e342.zip | |
Merge pull request #4306 from ReinUsesLisp/bsd-network
core/network: Add network abstraction
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/core/core.cpp | 4 | ||||
| -rw-r--r-- | src/core/network/network.cpp | 652 | ||||
| -rw-r--r-- | src/core/network/network.h | 87 | ||||
| -rw-r--r-- | src/core/network/sockets.h | 94 |
5 files changed, 840 insertions, 0 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c42f95705..c85c9485f 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -586,6 +586,9 @@ add_library(core STATIC | |||
| 586 | memory/dmnt_cheat_vm.h | 586 | memory/dmnt_cheat_vm.h |
| 587 | memory.cpp | 587 | memory.cpp |
| 588 | memory.h | 588 | memory.h |
| 589 | network/network.cpp | ||
| 590 | network/network.h | ||
| 591 | network/sockets.h | ||
| 589 | perf_stats.cpp | 592 | perf_stats.cpp |
| 590 | perf_stats.h | 593 | perf_stats.h |
| 591 | reporter.cpp | 594 | reporter.cpp |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 69a1aa0a5..e598c0e2b 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -43,6 +43,7 @@ | |||
| 43 | #include "core/loader/loader.h" | 43 | #include "core/loader/loader.h" |
| 44 | #include "core/memory.h" | 44 | #include "core/memory.h" |
| 45 | #include "core/memory/cheat_engine.h" | 45 | #include "core/memory/cheat_engine.h" |
| 46 | #include "core/network/network.h" | ||
| 46 | #include "core/perf_stats.h" | 47 | #include "core/perf_stats.h" |
| 47 | #include "core/reporter.h" | 48 | #include "core/reporter.h" |
| 48 | #include "core/settings.h" | 49 | #include "core/settings.h" |
| @@ -394,6 +395,9 @@ struct System::Impl { | |||
| 394 | /// Telemetry session for this emulation session | 395 | /// Telemetry session for this emulation session |
| 395 | std::unique_ptr<Core::TelemetrySession> telemetry_session; | 396 | std::unique_ptr<Core::TelemetrySession> telemetry_session; |
| 396 | 397 | ||
| 398 | /// Network instance | ||
| 399 | Network::NetworkInstance network_instance; | ||
| 400 | |||
| 397 | ResultStatus status = ResultStatus::Success; | 401 | ResultStatus status = ResultStatus::Success; |
| 398 | std::string status_details = ""; | 402 | std::string status_details = ""; |
| 399 | 403 | ||
diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp new file mode 100644 index 000000000..fb21b0a47 --- /dev/null +++ b/src/core/network/network.cpp | |||
| @@ -0,0 +1,652 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <cstring> | ||
| 7 | #include <limits> | ||
| 8 | #include <utility> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #ifdef _WIN32 | ||
| 12 | #define _WINSOCK_DEPRECATED_NO_WARNINGS // gethostname | ||
| 13 | #include <winsock2.h> | ||
| 14 | #elif __unix__ | ||
| 15 | #include <errno.h> | ||
| 16 | #include <fcntl.h> | ||
| 17 | #include <netdb.h> | ||
| 18 | #include <poll.h> | ||
| 19 | #include <unistd.h> | ||
| 20 | #else | ||
| 21 | #error "Unimplemented platform" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include "common/assert.h" | ||
| 25 | #include "common/common_types.h" | ||
| 26 | #include "common/logging/log.h" | ||
| 27 | #include "core/network/network.h" | ||
| 28 | #include "core/network/sockets.h" | ||
| 29 | |||
| 30 | namespace Network { | ||
| 31 | |||
| 32 | namespace { | ||
| 33 | |||
| 34 | #ifdef _WIN32 | ||
| 35 | |||
| 36 | using socklen_t = int; | ||
| 37 | |||
| 38 | void Initialize() { | ||
| 39 | WSADATA wsa_data; | ||
| 40 | (void)WSAStartup(MAKEWORD(2, 2), &wsa_data); | ||
| 41 | } | ||
| 42 | |||
| 43 | void Finalize() { | ||
| 44 | WSACleanup(); | ||
| 45 | } | ||
| 46 | |||
| 47 | constexpr IPv4Address TranslateIPv4(in_addr addr) { | ||
| 48 | auto& bytes = addr.S_un.S_un_b; | ||
| 49 | return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4}; | ||
| 50 | } | ||
| 51 | |||
| 52 | sockaddr TranslateFromSockAddrIn(SockAddrIn input) { | ||
| 53 | sockaddr_in result; | ||
| 54 | |||
| 55 | #ifdef __unix__ | ||
| 56 | result.sin_len = sizeof(result); | ||
| 57 | #endif | ||
| 58 | |||
| 59 | switch (static_cast<Domain>(input.family)) { | ||
| 60 | case Domain::INET: | ||
| 61 | result.sin_family = AF_INET; | ||
| 62 | break; | ||
| 63 | default: | ||
| 64 | UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", static_cast<int>(input.family)); | ||
| 65 | result.sin_family = AF_INET; | ||
| 66 | break; | ||
| 67 | } | ||
| 68 | |||
| 69 | result.sin_port = htons(input.portno); | ||
| 70 | |||
| 71 | auto& ip = result.sin_addr.S_un.S_un_b; | ||
| 72 | ip.s_b1 = input.ip[0]; | ||
| 73 | ip.s_b2 = input.ip[1]; | ||
| 74 | ip.s_b3 = input.ip[2]; | ||
| 75 | ip.s_b4 = input.ip[3]; | ||
| 76 | |||
| 77 | sockaddr addr; | ||
| 78 | std::memcpy(&addr, &result, sizeof(addr)); | ||
| 79 | return addr; | ||
| 80 | } | ||
| 81 | |||
| 82 | LINGER MakeLinger(bool enable, u32 linger_value) { | ||
| 83 | ASSERT(linger_value <= std::numeric_limits<u_short>::max()); | ||
| 84 | |||
| 85 | LINGER value; | ||
| 86 | value.l_onoff = enable ? 1 : 0; | ||
| 87 | value.l_linger = static_cast<u_short>(linger_value); | ||
| 88 | return value; | ||
| 89 | } | ||
| 90 | |||
| 91 | int LastError() { | ||
| 92 | return WSAGetLastError(); | ||
| 93 | } | ||
| 94 | |||
| 95 | bool EnableNonBlock(SOCKET fd, bool enable) { | ||
| 96 | u_long value = enable ? 1 : 0; | ||
| 97 | return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR; | ||
| 98 | } | ||
| 99 | |||
| 100 | #elif __unix__ // ^ _WIN32 v __unix__ | ||
| 101 | |||
| 102 | using SOCKET = int; | ||
| 103 | using WSAPOLLFD = pollfd; | ||
| 104 | using ULONG = u64; | ||
| 105 | |||
| 106 | constexpr SOCKET INVALID_SOCKET = -1; | ||
| 107 | constexpr SOCKET SOCKET_ERROR = -1; | ||
| 108 | |||
| 109 | constexpr int WSAEWOULDBLOCK = EAGAIN; | ||
| 110 | constexpr int WSAENOTCONN = ENOTCONN; | ||
| 111 | |||
| 112 | constexpr int SD_RECEIVE = SHUT_RD; | ||
| 113 | constexpr int SD_SEND = SHUT_WR; | ||
| 114 | constexpr int SD_BOTH = SHUT_RDWR; | ||
| 115 | |||
| 116 | void Initialize() {} | ||
| 117 | |||
| 118 | void Finalize() {} | ||
| 119 | |||
| 120 | constexpr IPv4Address TranslateIPv4(in_addr addr) { | ||
| 121 | const u32 bytes = addr.s_addr; | ||
| 122 | return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8), | ||
| 123 | static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)}; | ||
| 124 | } | ||
| 125 | |||
| 126 | sockaddr TranslateFromSockAddrIn(SockAddrIn input) { | ||
| 127 | sockaddr_in result; | ||
| 128 | |||
| 129 | switch (static_cast<Domain>(input.family)) { | ||
| 130 | case Domain::INET: | ||
| 131 | result.sin_family = AF_INET; | ||
| 132 | break; | ||
| 133 | default: | ||
| 134 | UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", static_cast<int>(input.family)); | ||
| 135 | result.sin_family = AF_INET; | ||
| 136 | break; | ||
| 137 | } | ||
| 138 | |||
| 139 | result.sin_port = htons(input.portno); | ||
| 140 | |||
| 141 | result.sin_addr.s_addr = input.ip[0] | input.ip[1] << 8 | input.ip[2] << 16 | input.ip[3] << 24; | ||
| 142 | |||
| 143 | sockaddr addr; | ||
| 144 | std::memcpy(&addr, &result, sizeof(addr)); | ||
| 145 | return addr; | ||
| 146 | } | ||
| 147 | |||
| 148 | int WSAPoll(WSAPOLLFD* fds, ULONG nfds, int timeout) { | ||
| 149 | return poll(fds, nfds, timeout); | ||
| 150 | } | ||
| 151 | |||
| 152 | int closesocket(SOCKET fd) { | ||
| 153 | return close(fd); | ||
| 154 | } | ||
| 155 | |||
| 156 | linger MakeLinger(bool enable, u32 linger_value) { | ||
| 157 | linger value; | ||
| 158 | value.l_onoff = enable ? 1 : 0; | ||
| 159 | value.l_linger = linger_value; | ||
| 160 | return value; | ||
| 161 | } | ||
| 162 | |||
| 163 | int LastError() { | ||
| 164 | return errno; | ||
| 165 | } | ||
| 166 | |||
| 167 | bool EnableNonBlock(int fd, bool enable) { | ||
| 168 | int flags = fcntl(fd, F_GETFD); | ||
| 169 | if (flags == -1) { | ||
| 170 | return false; | ||
| 171 | } | ||
| 172 | if (enable) { | ||
| 173 | flags |= O_NONBLOCK; | ||
| 174 | } else { | ||
| 175 | flags &= ~O_NONBLOCK; | ||
| 176 | } | ||
| 177 | return fcntl(fd, F_SETFD, flags) == 0; | ||
| 178 | } | ||
| 179 | |||
| 180 | #endif | ||
| 181 | |||
| 182 | int TranslateDomain(Domain domain) { | ||
| 183 | switch (domain) { | ||
| 184 | case Domain::INET: | ||
| 185 | return AF_INET; | ||
| 186 | default: | ||
| 187 | UNIMPLEMENTED_MSG("Unimplemented domain={}", static_cast<int>(domain)); | ||
| 188 | return 0; | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 192 | int TranslateType(Type type) { | ||
| 193 | switch (type) { | ||
| 194 | case Type::STREAM: | ||
| 195 | return SOCK_STREAM; | ||
| 196 | case Type::DGRAM: | ||
| 197 | return SOCK_DGRAM; | ||
| 198 | default: | ||
| 199 | UNIMPLEMENTED_MSG("Unimplemented type={}", static_cast<int>(type)); | ||
| 200 | return 0; | ||
| 201 | } | ||
| 202 | } | ||
| 203 | |||
| 204 | int TranslateProtocol(Protocol protocol) { | ||
| 205 | switch (protocol) { | ||
| 206 | case Protocol::TCP: | ||
| 207 | return IPPROTO_TCP; | ||
| 208 | case Protocol::UDP: | ||
| 209 | return IPPROTO_UDP; | ||
| 210 | default: | ||
| 211 | UNIMPLEMENTED_MSG("Unimplemented protocol={}", static_cast<int>(protocol)); | ||
| 212 | return 0; | ||
| 213 | } | ||
| 214 | } | ||
| 215 | |||
| 216 | SockAddrIn TranslateToSockAddrIn(sockaddr input_) { | ||
| 217 | sockaddr_in input; | ||
| 218 | std::memcpy(&input, &input_, sizeof(input)); | ||
| 219 | |||
| 220 | SockAddrIn result; | ||
| 221 | |||
| 222 | switch (input.sin_family) { | ||
| 223 | case AF_INET: | ||
| 224 | result.family = Domain::INET; | ||
| 225 | break; | ||
| 226 | default: | ||
| 227 | UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.sin_family); | ||
| 228 | result.family = Domain::INET; | ||
| 229 | break; | ||
| 230 | } | ||
| 231 | |||
| 232 | result.portno = ntohs(input.sin_port); | ||
| 233 | |||
| 234 | result.ip = TranslateIPv4(input.sin_addr); | ||
| 235 | |||
| 236 | return result; | ||
| 237 | } | ||
| 238 | |||
| 239 | u16 TranslatePollEvents(u16 events) { | ||
| 240 | u16 result = 0; | ||
| 241 | |||
| 242 | if (events & POLL_IN) { | ||
| 243 | events &= ~POLL_IN; | ||
| 244 | result |= POLLIN; | ||
| 245 | } | ||
| 246 | if (events & POLL_PRI) { | ||
| 247 | events &= ~POLL_PRI; | ||
| 248 | #ifdef _WIN32 | ||
| 249 | LOG_WARNING(Service, "Winsock doesn't support POLLPRI"); | ||
| 250 | #else | ||
| 251 | result |= POLL_PRI; | ||
| 252 | #endif | ||
| 253 | } | ||
| 254 | if (events & POLL_OUT) { | ||
| 255 | events &= ~POLL_OUT; | ||
| 256 | result |= POLLOUT; | ||
| 257 | } | ||
| 258 | |||
| 259 | UNIMPLEMENTED_IF_MSG(events != 0, "Unhandled guest events=0x{:x}", events); | ||
| 260 | |||
| 261 | return result; | ||
| 262 | } | ||
| 263 | |||
| 264 | u16 TranslatePollRevents(u16 revents) { | ||
| 265 | u16 result = 0; | ||
| 266 | const auto translate = [&result, &revents](int host, unsigned guest) { | ||
| 267 | if (revents & host) { | ||
| 268 | revents &= ~host; | ||
| 269 | result |= guest; | ||
| 270 | } | ||
| 271 | }; | ||
| 272 | |||
| 273 | translate(POLLIN, POLL_IN); | ||
| 274 | translate(POLLPRI, POLL_PRI); | ||
| 275 | translate(POLLOUT, POLL_OUT); | ||
| 276 | translate(POLLERR, POLL_ERR); | ||
| 277 | translate(POLLHUP, POLL_HUP); | ||
| 278 | |||
| 279 | UNIMPLEMENTED_IF_MSG(revents != 0, "Unhandled host revents=0x{:x}", revents); | ||
| 280 | |||
| 281 | return result; | ||
| 282 | } | ||
| 283 | |||
| 284 | template <typename T> | ||
| 285 | Errno SetSockOpt(SOCKET fd, int option, T value) { | ||
| 286 | const int result = | ||
| 287 | setsockopt(fd, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value)); | ||
| 288 | if (result != SOCKET_ERROR) { | ||
| 289 | return Errno::SUCCESS; | ||
| 290 | } | ||
| 291 | const int ec = LastError(); | ||
| 292 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 293 | return Errno::SUCCESS; | ||
| 294 | } | ||
| 295 | |||
| 296 | } // Anonymous namespace | ||
| 297 | |||
| 298 | NetworkInstance::NetworkInstance() { | ||
| 299 | Initialize(); | ||
| 300 | } | ||
| 301 | |||
| 302 | NetworkInstance::~NetworkInstance() { | ||
| 303 | Finalize(); | ||
| 304 | } | ||
| 305 | |||
| 306 | std::pair<IPv4Address, Errno> GetHostIPv4Address() { | ||
| 307 | std::array<char, 256> name{}; | ||
| 308 | if (gethostname(name.data(), static_cast<int>(name.size()) - 1) == SOCKET_ERROR) { | ||
| 309 | UNIMPLEMENTED_MSG("Unhandled gethostname error"); | ||
| 310 | return {IPv4Address{}, Errno::SUCCESS}; | ||
| 311 | } | ||
| 312 | |||
| 313 | hostent* const ent = gethostbyname(name.data()); | ||
| 314 | if (!ent) { | ||
| 315 | UNIMPLEMENTED_MSG("Unhandled gethostbyname error"); | ||
| 316 | return {IPv4Address{}, Errno::SUCCESS}; | ||
| 317 | } | ||
| 318 | if (ent->h_addr_list == nullptr) { | ||
| 319 | UNIMPLEMENTED_MSG("No addr provided in hostent->h_addr_list"); | ||
| 320 | return {IPv4Address{}, Errno::SUCCESS}; | ||
| 321 | } | ||
| 322 | if (ent->h_length != sizeof(in_addr)) { | ||
| 323 | UNIMPLEMENTED_MSG("Unexpected size={} in hostent->h_length", ent->h_length); | ||
| 324 | } | ||
| 325 | |||
| 326 | in_addr addr; | ||
| 327 | std::memcpy(&addr, ent->h_addr_list[0], sizeof(addr)); | ||
| 328 | return {TranslateIPv4(addr), Errno::SUCCESS}; | ||
| 329 | } | ||
| 330 | |||
| 331 | std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { | ||
| 332 | const size_t num = pollfds.size(); | ||
| 333 | |||
| 334 | std::vector<WSAPOLLFD> host_pollfds(pollfds.size()); | ||
| 335 | std::transform(pollfds.begin(), pollfds.end(), host_pollfds.begin(), [](PollFD fd) { | ||
| 336 | WSAPOLLFD result; | ||
| 337 | result.fd = fd.socket->fd; | ||
| 338 | result.events = TranslatePollEvents(fd.events); | ||
| 339 | result.revents = 0; | ||
| 340 | return result; | ||
| 341 | }); | ||
| 342 | |||
| 343 | const int result = WSAPoll(host_pollfds.data(), static_cast<ULONG>(num), timeout); | ||
| 344 | if (result == 0) { | ||
| 345 | ASSERT(std::all_of(host_pollfds.begin(), host_pollfds.end(), | ||
| 346 | [](WSAPOLLFD fd) { return fd.revents == 0; })); | ||
| 347 | return {0, Errno::SUCCESS}; | ||
| 348 | } | ||
| 349 | |||
| 350 | for (size_t i = 0; i < num; ++i) { | ||
| 351 | pollfds[i].revents = TranslatePollRevents(host_pollfds[i].revents); | ||
| 352 | } | ||
| 353 | |||
| 354 | if (result > 0) { | ||
| 355 | return {result, Errno::SUCCESS}; | ||
| 356 | } | ||
| 357 | |||
| 358 | ASSERT(result == SOCKET_ERROR); | ||
| 359 | |||
| 360 | const int ec = LastError(); | ||
| 361 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 362 | return {-1, Errno::SUCCESS}; | ||
| 363 | } | ||
| 364 | |||
| 365 | Socket::~Socket() { | ||
| 366 | if (fd == INVALID_SOCKET) { | ||
| 367 | return; | ||
| 368 | } | ||
| 369 | (void)closesocket(fd); | ||
| 370 | fd = INVALID_SOCKET; | ||
| 371 | } | ||
| 372 | |||
| 373 | Socket::Socket(Socket&& rhs) noexcept : fd{std::exchange(rhs.fd, INVALID_SOCKET)} {} | ||
| 374 | |||
| 375 | Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { | ||
| 376 | fd = socket(TranslateDomain(domain), TranslateType(type), TranslateProtocol(protocol)); | ||
| 377 | if (fd != INVALID_SOCKET) { | ||
| 378 | return Errno::SUCCESS; | ||
| 379 | } | ||
| 380 | |||
| 381 | const int ec = LastError(); | ||
| 382 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 383 | return Errno::SUCCESS; | ||
| 384 | } | ||
| 385 | |||
| 386 | std::pair<Socket::AcceptResult, Errno> Socket::Accept() { | ||
| 387 | sockaddr addr; | ||
| 388 | socklen_t addrlen = sizeof(addr); | ||
| 389 | const SOCKET new_socket = accept(fd, &addr, &addrlen); | ||
| 390 | |||
| 391 | if (new_socket == INVALID_SOCKET) { | ||
| 392 | const int ec = LastError(); | ||
| 393 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 394 | return {AcceptResult{}, Errno::SUCCESS}; | ||
| 395 | } | ||
| 396 | |||
| 397 | AcceptResult result; | ||
| 398 | result.socket = std::make_unique<Socket>(); | ||
| 399 | result.socket->fd = new_socket; | ||
| 400 | |||
| 401 | ASSERT(addrlen == sizeof(sockaddr_in)); | ||
| 402 | result.sockaddr_in = TranslateToSockAddrIn(addr); | ||
| 403 | |||
| 404 | return {std::move(result), Errno::SUCCESS}; | ||
| 405 | } | ||
| 406 | |||
| 407 | Errno Socket::Connect(SockAddrIn addr_in) { | ||
| 408 | const sockaddr host_addr_in = TranslateFromSockAddrIn(addr_in); | ||
| 409 | if (connect(fd, &host_addr_in, sizeof(host_addr_in)) != INVALID_SOCKET) { | ||
| 410 | return Errno::SUCCESS; | ||
| 411 | } | ||
| 412 | |||
| 413 | switch (const int ec = LastError()) { | ||
| 414 | case WSAEWOULDBLOCK: | ||
| 415 | LOG_DEBUG(Service, "EAGAIN generated"); | ||
| 416 | return Errno::AGAIN; | ||
| 417 | default: | ||
| 418 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 419 | return Errno::SUCCESS; | ||
| 420 | } | ||
| 421 | } | ||
| 422 | |||
| 423 | std::pair<SockAddrIn, Errno> Socket::GetPeerName() { | ||
| 424 | sockaddr addr; | ||
| 425 | socklen_t addrlen = sizeof(addr); | ||
| 426 | if (getpeername(fd, &addr, &addrlen) == SOCKET_ERROR) { | ||
| 427 | const int ec = LastError(); | ||
| 428 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 429 | return {SockAddrIn{}, Errno::SUCCESS}; | ||
| 430 | } | ||
| 431 | |||
| 432 | ASSERT(addrlen == sizeof(sockaddr_in)); | ||
| 433 | return {TranslateToSockAddrIn(addr), Errno::SUCCESS}; | ||
| 434 | } | ||
| 435 | |||
| 436 | std::pair<SockAddrIn, Errno> Socket::GetSockName() { | ||
| 437 | sockaddr addr; | ||
| 438 | socklen_t addrlen = sizeof(addr); | ||
| 439 | if (getsockname(fd, &addr, &addrlen) == SOCKET_ERROR) { | ||
| 440 | const int ec = LastError(); | ||
| 441 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 442 | return {SockAddrIn{}, Errno::SUCCESS}; | ||
| 443 | } | ||
| 444 | |||
| 445 | ASSERT(addrlen == sizeof(sockaddr_in)); | ||
| 446 | return {TranslateToSockAddrIn(addr), Errno::SUCCESS}; | ||
| 447 | } | ||
| 448 | |||
| 449 | Errno Socket::Bind(SockAddrIn addr) { | ||
| 450 | const sockaddr addr_in = TranslateFromSockAddrIn(addr); | ||
| 451 | if (bind(fd, &addr_in, sizeof(addr_in)) != SOCKET_ERROR) { | ||
| 452 | return Errno::SUCCESS; | ||
| 453 | } | ||
| 454 | |||
| 455 | const int ec = LastError(); | ||
| 456 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 457 | return Errno::SUCCESS; | ||
| 458 | } | ||
| 459 | |||
| 460 | Errno Socket::Listen(s32 backlog) { | ||
| 461 | if (listen(fd, backlog) != SOCKET_ERROR) { | ||
| 462 | return Errno::SUCCESS; | ||
| 463 | } | ||
| 464 | |||
| 465 | const int ec = LastError(); | ||
| 466 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 467 | return Errno::SUCCESS; | ||
| 468 | } | ||
| 469 | |||
| 470 | Errno Socket::Shutdown(ShutdownHow how) { | ||
| 471 | int host_how = 0; | ||
| 472 | switch (how) { | ||
| 473 | case ShutdownHow::RD: | ||
| 474 | host_how = SD_RECEIVE; | ||
| 475 | break; | ||
| 476 | case ShutdownHow::WR: | ||
| 477 | host_how = SD_SEND; | ||
| 478 | break; | ||
| 479 | case ShutdownHow::RDWR: | ||
| 480 | host_how = SD_BOTH; | ||
| 481 | break; | ||
| 482 | default: | ||
| 483 | UNIMPLEMENTED_MSG("Unimplemented flag how={}", static_cast<int>(how)); | ||
| 484 | return Errno::SUCCESS; | ||
| 485 | } | ||
| 486 | if (shutdown(fd, host_how) != SOCKET_ERROR) { | ||
| 487 | return Errno::SUCCESS; | ||
| 488 | } | ||
| 489 | |||
| 490 | switch (const int ec = LastError()) { | ||
| 491 | case WSAENOTCONN: | ||
| 492 | LOG_ERROR(Service, "ENOTCONN generated"); | ||
| 493 | return Errno::NOTCONN; | ||
| 494 | default: | ||
| 495 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 496 | return Errno::SUCCESS; | ||
| 497 | } | ||
| 498 | } | ||
| 499 | |||
| 500 | std::pair<s32, Errno> Socket::Recv(int flags, std::vector<u8>& message) { | ||
| 501 | ASSERT(flags == 0); | ||
| 502 | ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); | ||
| 503 | |||
| 504 | const int result = | ||
| 505 | recv(fd, reinterpret_cast<char*>(message.data()), static_cast<int>(message.size()), 0); | ||
| 506 | if (result != SOCKET_ERROR) { | ||
| 507 | return {result, Errno::SUCCESS}; | ||
| 508 | } | ||
| 509 | |||
| 510 | switch (const int ec = LastError()) { | ||
| 511 | case WSAEWOULDBLOCK: | ||
| 512 | LOG_DEBUG(Service, "EAGAIN generated"); | ||
| 513 | return {-1, Errno::AGAIN}; | ||
| 514 | case WSAENOTCONN: | ||
| 515 | LOG_ERROR(Service, "ENOTCONN generated"); | ||
| 516 | return {-1, Errno::NOTCONN}; | ||
| 517 | default: | ||
| 518 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 519 | return {0, Errno::SUCCESS}; | ||
| 520 | } | ||
| 521 | } | ||
| 522 | |||
| 523 | std::pair<s32, Errno> Socket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) { | ||
| 524 | ASSERT(flags == 0); | ||
| 525 | ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); | ||
| 526 | |||
| 527 | sockaddr addr_in{}; | ||
| 528 | socklen_t addrlen = sizeof(addr_in); | ||
| 529 | socklen_t* const p_addrlen = addr ? &addrlen : nullptr; | ||
| 530 | sockaddr* const p_addr_in = addr ? &addr_in : nullptr; | ||
| 531 | |||
| 532 | const int result = recvfrom(fd, reinterpret_cast<char*>(message.data()), | ||
| 533 | static_cast<int>(message.size()), 0, p_addr_in, p_addrlen); | ||
| 534 | if (result != SOCKET_ERROR) { | ||
| 535 | if (addr) { | ||
| 536 | ASSERT(addrlen == sizeof(addr_in)); | ||
| 537 | *addr = TranslateToSockAddrIn(addr_in); | ||
| 538 | } | ||
| 539 | return {result, Errno::SUCCESS}; | ||
| 540 | } | ||
| 541 | |||
| 542 | switch (const int ec = LastError()) { | ||
| 543 | case WSAEWOULDBLOCK: | ||
| 544 | LOG_DEBUG(Service, "EAGAIN generated"); | ||
| 545 | return {-1, Errno::AGAIN}; | ||
| 546 | case WSAENOTCONN: | ||
| 547 | LOG_ERROR(Service, "ENOTCONN generated"); | ||
| 548 | return {-1, Errno::NOTCONN}; | ||
| 549 | default: | ||
| 550 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 551 | return {-1, Errno::SUCCESS}; | ||
| 552 | } | ||
| 553 | } | ||
| 554 | |||
| 555 | std::pair<s32, Errno> Socket::Send(const std::vector<u8>& message, int flags) { | ||
| 556 | ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); | ||
| 557 | ASSERT(flags == 0); | ||
| 558 | |||
| 559 | const int result = send(fd, reinterpret_cast<const char*>(message.data()), | ||
| 560 | static_cast<int>(message.size()), 0); | ||
| 561 | if (result != SOCKET_ERROR) { | ||
| 562 | return {result, Errno::SUCCESS}; | ||
| 563 | } | ||
| 564 | |||
| 565 | const int ec = LastError(); | ||
| 566 | switch (ec) { | ||
| 567 | case WSAEWOULDBLOCK: | ||
| 568 | LOG_DEBUG(Service, "EAGAIN generated"); | ||
| 569 | return {-1, Errno::AGAIN}; | ||
| 570 | case WSAENOTCONN: | ||
| 571 | LOG_ERROR(Service, "ENOTCONN generated"); | ||
| 572 | return {-1, Errno::NOTCONN}; | ||
| 573 | default: | ||
| 574 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 575 | return {-1, Errno::SUCCESS}; | ||
| 576 | } | ||
| 577 | } | ||
| 578 | |||
| 579 | std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message, | ||
| 580 | const SockAddrIn* addr) { | ||
| 581 | ASSERT(flags == 0); | ||
| 582 | |||
| 583 | const sockaddr* to = nullptr; | ||
| 584 | const int tolen = addr ? 0 : sizeof(sockaddr); | ||
| 585 | sockaddr host_addr_in; | ||
| 586 | |||
| 587 | if (addr) { | ||
| 588 | host_addr_in = TranslateFromSockAddrIn(*addr); | ||
| 589 | to = &host_addr_in; | ||
| 590 | } | ||
| 591 | |||
| 592 | const int result = sendto(fd, reinterpret_cast<const char*>(message.data()), | ||
| 593 | static_cast<int>(message.size()), 0, to, tolen); | ||
| 594 | if (result != SOCKET_ERROR) { | ||
| 595 | return {result, Errno::SUCCESS}; | ||
| 596 | } | ||
| 597 | |||
| 598 | const int ec = LastError(); | ||
| 599 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 600 | return {-1, Errno::SUCCESS}; | ||
| 601 | } | ||
| 602 | |||
| 603 | Errno Socket::Close() { | ||
| 604 | [[maybe_unused]] const int result = closesocket(fd); | ||
| 605 | ASSERT(result == 0); | ||
| 606 | fd = INVALID_SOCKET; | ||
| 607 | |||
| 608 | return Errno::SUCCESS; | ||
| 609 | } | ||
| 610 | |||
| 611 | Errno Socket::SetLinger(bool enable, u32 linger) { | ||
| 612 | return SetSockOpt(fd, SO_LINGER, MakeLinger(enable, linger)); | ||
| 613 | } | ||
| 614 | |||
| 615 | Errno Socket::SetReuseAddr(bool enable) { | ||
| 616 | return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0); | ||
| 617 | } | ||
| 618 | |||
| 619 | Errno Socket::SetBroadcast(bool enable) { | ||
| 620 | return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0); | ||
| 621 | } | ||
| 622 | |||
| 623 | Errno Socket::SetSndBuf(u32 value) { | ||
| 624 | return SetSockOpt(fd, SO_SNDBUF, value); | ||
| 625 | } | ||
| 626 | |||
| 627 | Errno Socket::SetRcvBuf(u32 value) { | ||
| 628 | return SetSockOpt(fd, SO_RCVBUF, value); | ||
| 629 | } | ||
| 630 | |||
| 631 | Errno Socket::SetSndTimeo(u32 value) { | ||
| 632 | return SetSockOpt(fd, SO_SNDTIMEO, value); | ||
| 633 | } | ||
| 634 | |||
| 635 | Errno Socket::SetRcvTimeo(u32 value) { | ||
| 636 | return SetSockOpt(fd, SO_RCVTIMEO, value); | ||
| 637 | } | ||
| 638 | |||
| 639 | Errno Socket::SetNonBlock(bool enable) { | ||
| 640 | if (EnableNonBlock(fd, enable)) { | ||
| 641 | return Errno::SUCCESS; | ||
| 642 | } | ||
| 643 | const int ec = LastError(); | ||
| 644 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 645 | return Errno::SUCCESS; | ||
| 646 | } | ||
| 647 | |||
| 648 | bool Socket::IsOpened() const { | ||
| 649 | return fd != INVALID_SOCKET; | ||
| 650 | } | ||
| 651 | |||
| 652 | } // namespace Network | ||
diff --git a/src/core/network/network.h b/src/core/network/network.h new file mode 100644 index 000000000..0622e4593 --- /dev/null +++ b/src/core/network/network.h | |||
| @@ -0,0 +1,87 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <utility> | ||
| 9 | |||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace Network { | ||
| 13 | |||
| 14 | class Socket; | ||
| 15 | |||
| 16 | /// Error code for network functions | ||
| 17 | enum class Errno { | ||
| 18 | SUCCESS, | ||
| 19 | BADF, | ||
| 20 | INVAL, | ||
| 21 | MFILE, | ||
| 22 | NOTCONN, | ||
| 23 | AGAIN, | ||
| 24 | }; | ||
| 25 | |||
| 26 | /// Address families | ||
| 27 | enum class Domain { | ||
| 28 | INET, ///< Address family for IPv4 | ||
| 29 | }; | ||
| 30 | |||
| 31 | /// Socket types | ||
| 32 | enum class Type { | ||
| 33 | STREAM, | ||
| 34 | DGRAM, | ||
| 35 | RAW, | ||
| 36 | SEQPACKET, | ||
| 37 | }; | ||
| 38 | |||
| 39 | /// Protocol values for sockets | ||
| 40 | enum class Protocol { | ||
| 41 | ICMP, | ||
| 42 | TCP, | ||
| 43 | UDP, | ||
| 44 | }; | ||
| 45 | |||
| 46 | /// Shutdown mode | ||
| 47 | enum class ShutdownHow { | ||
| 48 | RD, | ||
| 49 | WR, | ||
| 50 | RDWR, | ||
| 51 | }; | ||
| 52 | |||
| 53 | /// Array of IPv4 address | ||
| 54 | using IPv4Address = std::array<u8, 4>; | ||
| 55 | |||
| 56 | /// Cross-platform sockaddr structure | ||
| 57 | struct SockAddrIn { | ||
| 58 | Domain family; | ||
| 59 | IPv4Address ip; | ||
| 60 | u16 portno; | ||
| 61 | }; | ||
| 62 | |||
| 63 | /// Cross-platform poll fd structure | ||
| 64 | struct PollFD { | ||
| 65 | Socket* socket; | ||
| 66 | u16 events; | ||
| 67 | u16 revents; | ||
| 68 | }; | ||
| 69 | |||
| 70 | constexpr u16 POLL_IN = 1 << 0; | ||
| 71 | constexpr u16 POLL_PRI = 1 << 1; | ||
| 72 | constexpr u16 POLL_OUT = 1 << 2; | ||
| 73 | constexpr u16 POLL_ERR = 1 << 3; | ||
| 74 | constexpr u16 POLL_HUP = 1 << 4; | ||
| 75 | constexpr u16 POLL_NVAL = 1 << 5; | ||
| 76 | |||
| 77 | class NetworkInstance { | ||
| 78 | public: | ||
| 79 | explicit NetworkInstance(); | ||
| 80 | ~NetworkInstance(); | ||
| 81 | }; | ||
| 82 | |||
| 83 | /// @brief Returns host's IPv4 address | ||
| 84 | /// @return Pair of an array of human ordered IPv4 address (e.g. 192.168.0.1) and an error code | ||
| 85 | std::pair<IPv4Address, Errno> GetHostIPv4Address(); | ||
| 86 | |||
| 87 | } // namespace Network | ||
diff --git a/src/core/network/sockets.h b/src/core/network/sockets.h new file mode 100644 index 000000000..7bdff0fe4 --- /dev/null +++ b/src/core/network/sockets.h | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <utility> | ||
| 9 | |||
| 10 | #if defined(_WIN32) | ||
| 11 | #include <winsock.h> | ||
| 12 | #elif !defined(__unix__) | ||
| 13 | #error "Platform not implemented" | ||
| 14 | #endif | ||
| 15 | |||
| 16 | #include "common/common_types.h" | ||
| 17 | #include "core/network/network.h" | ||
| 18 | |||
| 19 | // TODO: C++20 Replace std::vector usages with std::span | ||
| 20 | |||
| 21 | namespace Network { | ||
| 22 | |||
| 23 | class Socket { | ||
| 24 | public: | ||
| 25 | struct AcceptResult { | ||
| 26 | std::unique_ptr<Socket> socket; | ||
| 27 | SockAddrIn sockaddr_in; | ||
| 28 | }; | ||
| 29 | |||
| 30 | explicit Socket() = default; | ||
| 31 | ~Socket(); | ||
| 32 | |||
| 33 | Socket(const Socket&) = delete; | ||
| 34 | Socket& operator=(const Socket&) = delete; | ||
| 35 | |||
| 36 | Socket(Socket&& rhs) noexcept; | ||
| 37 | |||
| 38 | // Avoid closing sockets implicitly | ||
| 39 | Socket& operator=(Socket&&) noexcept = delete; | ||
| 40 | |||
| 41 | Errno Initialize(Domain domain, Type type, Protocol protocol); | ||
| 42 | |||
| 43 | Errno Close(); | ||
| 44 | |||
| 45 | std::pair<AcceptResult, Errno> Accept(); | ||
| 46 | |||
| 47 | Errno Connect(SockAddrIn addr_in); | ||
| 48 | |||
| 49 | std::pair<SockAddrIn, Errno> GetPeerName(); | ||
| 50 | |||
| 51 | std::pair<SockAddrIn, Errno> GetSockName(); | ||
| 52 | |||
| 53 | Errno Bind(SockAddrIn addr); | ||
| 54 | |||
| 55 | Errno Listen(s32 backlog); | ||
| 56 | |||
| 57 | Errno Shutdown(ShutdownHow how); | ||
| 58 | |||
| 59 | std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message); | ||
| 60 | |||
| 61 | std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr); | ||
| 62 | |||
| 63 | std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags); | ||
| 64 | |||
| 65 | std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, const SockAddrIn* addr); | ||
| 66 | |||
| 67 | Errno SetLinger(bool enable, u32 linger); | ||
| 68 | |||
| 69 | Errno SetReuseAddr(bool enable); | ||
| 70 | |||
| 71 | Errno SetBroadcast(bool enable); | ||
| 72 | |||
| 73 | Errno SetSndBuf(u32 value); | ||
| 74 | |||
| 75 | Errno SetRcvBuf(u32 value); | ||
| 76 | |||
| 77 | Errno SetSndTimeo(u32 value); | ||
| 78 | |||
| 79 | Errno SetRcvTimeo(u32 value); | ||
| 80 | |||
| 81 | Errno SetNonBlock(bool enable); | ||
| 82 | |||
| 83 | bool IsOpened() const; | ||
| 84 | |||
| 85 | #if defined(_WIN32) | ||
| 86 | SOCKET fd = INVALID_SOCKET; | ||
| 87 | #elif defined(__unix__) | ||
| 88 | int fd = -1; | ||
| 89 | #endif | ||
| 90 | }; | ||
| 91 | |||
| 92 | std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout); | ||
| 93 | |||
| 94 | } // namespace Network | ||