diff options
Diffstat (limited to 'src/osd/modules/file/winrtsocket.cpp')
-rw-r--r-- | src/osd/modules/file/winrtsocket.cpp | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/src/osd/modules/file/winrtsocket.cpp b/src/osd/modules/file/winrtsocket.cpp new file mode 100644 index 00000000000..94a8c3c3dc0 --- /dev/null +++ b/src/osd/modules/file/winrtsocket.cpp @@ -0,0 +1,274 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles, Vas Crabb +//============================================================ +// +// winsocket.c - Windows socket (inet) access functions +// +//============================================================ + +#define WIN32_LEAN_AND_MEAN + +#include "winfile.h" + +// MAME headers +#include "osdcore.h" + +#include <cassert> +#include <cstdio> + +// standard windows headers +#include <windows.h> +#include <winioctl.h> +#include <tchar.h> +#include <stdlib.h> +#include <ctype.h> + + +namespace { +char const *const winfile_socket_identifier = "socket."; + + +class win_osd_socket : public osd_file +{ +public: + win_osd_socket(win_osd_socket const &) = delete; + win_osd_socket(win_osd_socket &&) = delete; + win_osd_socket& operator=(win_osd_socket const &) = delete; + win_osd_socket& operator=(win_osd_socket &&) = delete; + + win_osd_socket(SOCKET s, bool l) + : m_socket(s) + , m_listening(l) + { + assert(INVALID_SOCKET != m_socket); + } + + virtual ~win_osd_socket() override + { + closesocket(m_socket); + } + + virtual error read(void *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual) override + { + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(m_socket, &readfds); + + struct timeval timeout; + timeout.tv_sec = timeout.tv_usec = 0; + + if (select(m_socket + 1, &readfds, nullptr, nullptr, &timeout) < 0) + { + char line[80]; + std::sprintf(line, "win_read_socket : %s : %d ", __FILE__, __LINE__); + std::perror(line); + return error::FAILURE; + } + else if (FD_ISSET(m_socket, &readfds)) + { + if (!m_listening) + { + // connected socket + int const result = recv(m_socket, (char*)buffer, length, 0); + if (result < 0) + { + return wsa_error_to_file_error(WSAGetLastError()); + } + else + { + actual = result; + return error::NONE; + } + } + else + { + // listening socket + SOCKET const accepted = accept(m_socket, nullptr, nullptr); + if (INVALID_SOCKET == accepted) + { + return wsa_error_to_file_error(WSAGetLastError()); + } + else + { + closesocket(m_socket); + m_socket = accepted; + m_listening = false; + actual = 0; + + return error::NONE; + } + } + } + else + { + return error::FAILURE; + } + } + + virtual error write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual) override + { + auto const result = send(m_socket, reinterpret_cast<const char *>(buffer), length, 0); + if (result < 0) + return wsa_error_to_file_error(WSAGetLastError()); + + actual = result; + return error::NONE; + } + + virtual error truncate(std::uint64_t offset) override + { + // doesn't make sense for a socket + return error::INVALID_ACCESS; + } + + virtual error flush() override + { + // no buffers to flush + return error::NONE; + } + + static error wsa_error_to_file_error(int err) + { + switch (err) + { + case 0: return error::NONE; + case WSAEACCES: return error::ACCESS_DENIED; + case WSAEADDRINUSE: return error::ALREADY_OPEN; + case WSAEADDRNOTAVAIL: return error::NOT_FOUND; + case WSAECONNREFUSED: return error::NOT_FOUND; + case WSAEHOSTUNREACH: return error::NOT_FOUND; + case WSAENETUNREACH: return error::NOT_FOUND; + default: return error::FAILURE; + } + } + +private: + SOCKET m_socket; + bool m_listening; +}; + +} // anonymous namespace + + +bool win_init_sockets() +{ + WSADATA wsaData; + WORD const version = MAKEWORD(2, 0); + int const error = WSAStartup(version, &wsaData); + + // check for error + if (error) + { + // error occurred + return false; + } + + // check for correct version + if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion ) != 0) + { + // incorrect WinSock version + WSACleanup(); + return false; + } + + // WinSock has been initialized + return true; +} + + +void win_cleanup_sockets() +{ + WSACleanup(); +} + + +bool win_check_socket_path(std::string const &path) +{ + if (strncmp(path.c_str(), winfile_socket_identifier, strlen(winfile_socket_identifier)) == 0 && + strchr(path.c_str(), ':') != nullptr) return true; + return false; +} + + +osd_file::error win_open_socket(std::string const &path, std::uint32_t openflags, osd_file::ptr &file, std::uint64_t &filesize) +{ + char hostname[256]; + int port; + std::sscanf(&path[strlen(winfile_socket_identifier)], "%255[^:]:%d", hostname, &port); + + struct hostent const *const localhost = gethostbyname(hostname); + if (!localhost) + return osd_file::error::NOT_FOUND; + + struct sockaddr_in sai; + memset(&sai, 0, sizeof(sai)); + sai.sin_family = AF_INET; + sai.sin_port = htons(port); + sai.sin_addr = *reinterpret_cast<struct in_addr *>(localhost->h_addr); + + SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); + if (INVALID_SOCKET == sock) + return win_osd_socket::wsa_error_to_file_error(WSAGetLastError()); + + int const flag = 1; + if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<const char *>(&flag), sizeof(flag)) == SOCKET_ERROR) + { + int const err = WSAGetLastError(); + closesocket(sock); + return win_osd_socket::wsa_error_to_file_error(err); + } + + // listening socket support + if (openflags & OPEN_FLAG_CREATE) + { + //printf("Listening for client at '%s' on port '%d'\n", hostname, port); + // bind socket... + if (bind(sock, reinterpret_cast<struct sockaddr const *>(&sai), sizeof(struct sockaddr)) == SOCKET_ERROR) + { + int const err = WSAGetLastError(); + closesocket(sock); + return win_osd_socket::wsa_error_to_file_error(err); + } + + // start to listen... + if (listen(sock, 1) == SOCKET_ERROR) + { + int const err = WSAGetLastError(); + closesocket(sock); + return win_osd_socket::wsa_error_to_file_error(err); + } + + // mark socket as "listening" + try + { + file = std::make_unique<win_osd_socket>(sock, true); + filesize = 0; + return osd_file::error::NONE; + } + catch (...) + { + closesocket(sock); + return osd_file::error::OUT_OF_MEMORY; + } + } + else + { + //printf("Connecting to server '%s' on port '%d'\n", hostname, port); + if (connect(sock, reinterpret_cast<struct sockaddr const *>(&sai), sizeof(struct sockaddr)) == SOCKET_ERROR) + { + closesocket(sock); + return osd_file::error::ACCESS_DENIED; // have to return this value or bitb won't try to bind on connect failure + } + try + { + file = std::make_unique<win_osd_socket>(sock, false); + filesize = 0; + return osd_file::error::NONE; + } + catch (...) + { + closesocket(sock); + return osd_file::error::OUT_OF_MEMORY; + } + } +} |