1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles, Vas Crabb
3 //============================================================
4 //
5 // winsocket.c - Windows socket (inet) access functions
6 //
7 //============================================================
8
9
10 #include "winfile.h"
11
12 // MAMEOS headers
13 #include "winutil.h"
14
15 // MAME headers
16 #include "osdcore.h"
17
18 #include <cassert>
19 #include <cstdio>
20
21 // standard windows headers
22 #include <windows.h>
23 #include <winioctl.h>
24 #include <tchar.h>
25 #include <cstdlib>
26 #include <cctype>
27
28
29 namespace {
30 char const *const winfile_socket_identifier = "socket.";
31
32
33 class win_osd_socket : public osd_file
34 {
35 public:
36 win_osd_socket(win_osd_socket const &) = delete;
37 win_osd_socket(win_osd_socket &&) = delete;
38 win_osd_socket& operator=(win_osd_socket const &) = delete;
39 win_osd_socket& operator=(win_osd_socket &&) = delete;
40
win_osd_socket(SOCKET s,bool l)41 win_osd_socket(SOCKET s, bool l)
42 : m_socket(s)
43 , m_listening(l)
44 {
45 assert(INVALID_SOCKET != m_socket);
46 }
47
~win_osd_socket()48 virtual ~win_osd_socket() override
49 {
50 closesocket(m_socket);
51 }
52
read(void * buffer,std::uint64_t offset,std::uint32_t length,std::uint32_t & actual)53 virtual error read(void *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual) override
54 {
55 fd_set readfds;
56 FD_ZERO(&readfds);
57 FD_SET(m_socket, &readfds);
58
59 struct timeval timeout;
60 timeout.tv_sec = timeout.tv_usec = 0;
61
62 if (select(m_socket + 1, &readfds, nullptr, nullptr, &timeout) < 0)
63 {
64 char line[80];
65 std::sprintf(line, "win_read_socket : %s : %d ", __FILE__, __LINE__);
66 std::perror(line);
67 return error::FAILURE;
68 }
69 else if (FD_ISSET(m_socket, &readfds))
70 {
71 if (!m_listening)
72 {
73 // connected socket
74 int const result = recv(m_socket, (char*)buffer, length, 0);
75 if (result < 0)
76 {
77 return wsa_error_to_file_error(WSAGetLastError());
78 }
79 else
80 {
81 actual = result;
82 return error::NONE;
83 }
84 }
85 else
86 {
87 // listening socket
88 SOCKET const accepted = accept(m_socket, nullptr, nullptr);
89 if (INVALID_SOCKET == accepted)
90 {
91 return wsa_error_to_file_error(WSAGetLastError());
92 }
93 else
94 {
95 closesocket(m_socket);
96 m_socket = accepted;
97 m_listening = false;
98 actual = 0;
99
100 return error::NONE;
101 }
102 }
103 }
104 else
105 {
106 return error::FAILURE;
107 }
108 }
109
write(void const * buffer,std::uint64_t offset,std::uint32_t length,std::uint32_t & actual)110 virtual error write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual) override
111 {
112 auto const result = send(m_socket, reinterpret_cast<const char *>(buffer), length, 0);
113 if (result < 0)
114 return wsa_error_to_file_error(WSAGetLastError());
115
116 actual = result;
117 return error::NONE;
118 }
119
truncate(std::uint64_t offset)120 virtual error truncate(std::uint64_t offset) override
121 {
122 // doesn't make sense for a socket
123 return error::INVALID_ACCESS;
124 }
125
flush()126 virtual error flush() override
127 {
128 // no buffers to flush
129 return error::NONE;
130 }
131
wsa_error_to_file_error(int err)132 static error wsa_error_to_file_error(int err)
133 {
134 switch (err)
135 {
136 case 0: return error::NONE;
137 case WSAEACCES: return error::ACCESS_DENIED;
138 case WSAEADDRINUSE: return error::ALREADY_OPEN;
139 case WSAEADDRNOTAVAIL: return error::NOT_FOUND;
140 case WSAECONNREFUSED: return error::NOT_FOUND;
141 case WSAEHOSTUNREACH: return error::NOT_FOUND;
142 case WSAENETUNREACH: return error::NOT_FOUND;
143 default: return error::FAILURE;
144 }
145 }
146
147 private:
148 SOCKET m_socket;
149 bool m_listening;
150 };
151
152 } // anonymous namespace
153
154
win_init_sockets()155 bool win_init_sockets()
156 {
157 WSADATA wsaData;
158 WORD const version = MAKEWORD(2, 0);
159 int const error = WSAStartup(version, &wsaData);
160
161 // check for error
162 if (error)
163 {
164 // error occurred
165 return false;
166 }
167
168 // check for correct version
169 if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion ) != 0)
170 {
171 // incorrect WinSock version
172 WSACleanup();
173 return false;
174 }
175
176 // WinSock has been initialized
177 return true;
178 }
179
180
win_cleanup_sockets()181 void win_cleanup_sockets()
182 {
183 WSACleanup();
184 }
185
186
win_check_socket_path(std::string const & path)187 bool win_check_socket_path(std::string const &path)
188 {
189 if (strncmp(path.c_str(), winfile_socket_identifier, strlen(winfile_socket_identifier)) == 0 &&
190 strchr(path.c_str(), ':') != nullptr) return true;
191 return false;
192 }
193
194
win_open_socket(std::string const & path,std::uint32_t openflags,osd_file::ptr & file,std::uint64_t & filesize)195 osd_file::error win_open_socket(std::string const &path, std::uint32_t openflags, osd_file::ptr &file, std::uint64_t &filesize)
196 {
197 char hostname[256];
198 int port;
199 std::sscanf(&path[strlen(winfile_socket_identifier)], "%255[^:]:%d", hostname, &port);
200
201 struct hostent const *const localhost = gethostbyname(hostname);
202 if (!localhost)
203 return osd_file::error::NOT_FOUND;
204
205 struct sockaddr_in sai;
206 memset(&sai, 0, sizeof(sai));
207 sai.sin_family = AF_INET;
208 sai.sin_port = htons(port);
209 sai.sin_addr = *reinterpret_cast<struct in_addr *>(localhost->h_addr);
210
211 SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
212 if (INVALID_SOCKET == sock)
213 return win_osd_socket::wsa_error_to_file_error(WSAGetLastError());
214
215 int const flag = 1;
216 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<const char *>(&flag), sizeof(flag)) == SOCKET_ERROR)
217 {
218 int const err = WSAGetLastError();
219 closesocket(sock);
220 return win_osd_socket::wsa_error_to_file_error(err);
221 }
222
223 // listening socket support
224 if (openflags & OPEN_FLAG_CREATE)
225 {
226 //printf("Listening for client at '%s' on port '%d'\n", hostname, port);
227 // bind socket...
228 if (bind(sock, reinterpret_cast<struct sockaddr const *>(&sai), sizeof(struct sockaddr)) == SOCKET_ERROR)
229 {
230 int const err = WSAGetLastError();
231 closesocket(sock);
232 return win_osd_socket::wsa_error_to_file_error(err);
233 }
234
235 // start to listen...
236 if (listen(sock, 1) == SOCKET_ERROR)
237 {
238 int const err = WSAGetLastError();
239 closesocket(sock);
240 return win_osd_socket::wsa_error_to_file_error(err);
241 }
242
243 // mark socket as "listening"
244 try
245 {
246 file = std::make_unique<win_osd_socket>(sock, true);
247 filesize = 0;
248 return osd_file::error::NONE;
249 }
250 catch (...)
251 {
252 closesocket(sock);
253 return osd_file::error::OUT_OF_MEMORY;
254 }
255 }
256 else
257 {
258 //printf("Connecting to server '%s' on port '%d'\n", hostname, port);
259 if (connect(sock, reinterpret_cast<struct sockaddr const *>(&sai), sizeof(struct sockaddr)) == SOCKET_ERROR)
260 {
261 closesocket(sock);
262 return osd_file::error::ACCESS_DENIED; // have to return this value or bitb won't try to bind on connect failure
263 }
264 try
265 {
266 file = std::make_unique<win_osd_socket>(sock, false);
267 filesize = 0;
268 return osd_file::error::NONE;
269 }
270 catch (...)
271 {
272 closesocket(sock);
273 return osd_file::error::OUT_OF_MEMORY;
274 }
275 }
276 }
277