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