OXYGENHUB
◆ main openmsx / src / events / Socket.cc
3.7 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "Socket.hh"
2
3
#include "MSXException.hh" // FatalError
4
#include "utf8_checked.hh"
5
6
#include <bit>
7
#include <cerrno>
8
#include <cstring>
9
10
#ifndef _WIN32
11
#include <netinet/tcp.h> // TCP_NODELAY (winsock2.h provides it on Windows)
12
#endif
13
14
namespace openmsx {
15
16
std::string sock_error()
17
{
18
#ifdef _WIN32
19
	wchar_t* s = nullptr;
20
	FormatMessageW(
21
		FORMAT_MESSAGE_ALLOCATE_BUFFER |
22
		FORMAT_MESSAGE_FROM_SYSTEM |
23
		FORMAT_MESSAGE_IGNORE_INSERTS,
24
		nullptr, WSAGetLastError(), 0, reinterpret_cast<LPWSTR>(&s),
25
		0, nullptr);
26
	std::string result = utf8::utf16to8(s);
27
	LocalFree(s);
28
	return result;
29
#else
30
	return strerror(errno);
31
#endif
32
}
33
34
void sock_startup()
35
{
36
#ifdef _WIN32
37
	// MAKEWORD is #define'd as ((WORD)(((BYTE)(a))|(((WORD)((BYTE)(b)))<<8)))
38
	// but using that gives old-style-cast warnings
39
	WORD w = 1 | (1 << 8); // MAKEWORD(1, 1)
40
	WSAData wsaData;
41
	if (WSAStartup(w, &wsaData) != 0) {
42
		throw FatalError(sock_error());
43
	}
44
#else
45
	// nothing needed for unix
46
#endif
47
}
48
49
void sock_cleanup()
50
{
51
#ifdef _WIN32
52
	WSACleanup();
53
#else
54
	// nothing needed for unix
55
#endif
56
}
57
58
// close a connection
59
void sock_close(SOCKET sd)
60
{
61
#ifdef _WIN32
62
	closesocket(sd);
63
#else
64
	close(sd);
65
#endif
66
}
67
68
69
ptrdiff_t sock_recv(SOCKET sd, char* buf, size_t count)
70
{
71
	ptrdiff_t num = recv(sd, buf, count, 0);
72
	if (num >  0) return num; // normal case
73
	if (num == 0) return -1;  // socket was closed by client
74
#ifdef _WIN32
75
	// Something bad happened on the socket.  It could just be a
76
	// "would block" notification, or it could be something more
77
	// serious.  WSAEWOULDBLOCK can happen after select() says a
78
	// socket is readable under Win9x, it doesn't happen on
79
	// WinNT/2000 or on Unix.
80
	int err;
81
	int errLen = sizeof(err);
82
	getsockopt(sd, SOL_SOCKET, SO_ERROR, std::bit_cast<char*>(&err), &errLen);
83
	if (err == WSAEWOULDBLOCK) return 0;
84
	return -1;
85
#else
86
	if (errno == EWOULDBLOCK) return 0;
87
	return -1;
88
#endif
89
}
90
91
92
ptrdiff_t sock_send(SOCKET sd, const char* buf, size_t count)
93
{
94
	ptrdiff_t num = send(sd, buf, count, 0);
95
	if (num >= 0) return num; // normal case
96
#ifdef _WIN32
97
	int err;
98
	int errLen = sizeof(err);
99
	getsockopt(sd, SOL_SOCKET, SO_ERROR, std::bit_cast<char*>(&err), &errLen);
100
	if (err == WSAEWOULDBLOCK) return 0;
101
	return -1;
102
#else
103
	if (errno == EWOULDBLOCK) return 0;
104
	return -1;
105
#endif
106
}
107
108
void sock_setNonBlocking(SOCKET sd)
109
{
110
#ifdef _WIN32
111
	u_long mode = 1;
112
	ioctlsocket(sd, FIONBIO, &mode);
113
#else
114
	int flags = fcntl(sd, F_GETFL, 0);
115
	fcntl(sd, F_SETFL, flags | O_NONBLOCK);
116
#endif
117
}
118
119
void sock_setIntOption(SOCKET sd, int level, int optName, int value)
120
{
121
	setsockopt(sd, level, optName, std::bit_cast<const char*>(&value), sizeof(value));
122
}
123
124
int sock_getIntOption(SOCKET sd, int level, int optName)
125
{
126
	int value = 0;
127
	::socklen_t len = sizeof(value);
128
	if (getsockopt(sd, level, optName, std::bit_cast<char*>(&value), &len) != 0) {
129
		return 0;
130
	}
131
	return value;
132
}
133
134
void sock_setNoDelay(SOCKET sd)
135
{
136
	sock_setIntOption(sd, IPPROTO_TCP, TCP_NODELAY);
137
}
138
139
bool sock_readable(SOCKET sd)
140
{
141
	fd_set rfds;
142
	FD_ZERO(&rfds);
143
	FD_SET(sd, &rfds);
144
	timeval tv = {0, 0};
145
	return select(static_cast<int>(sd) + 1, &rfds, nullptr, nullptr, &tv) > 0;
146
}
147
148
sockaddr_in sock_makeIPv4(uint32_t hostIp, uint16_t port)
149
{
150
	sockaddr_in addr = {};
151
	addr.sin_family = AF_INET;
152
	addr.sin_addr.s_addr = htonl(hostIp);
153
	addr.sin_port = htons(port);
154
	return addr;
155
}
156
157
uint32_t sock_localIPv4()
158
{
159
	SOCKET sd = socket(AF_INET, SOCK_DGRAM, 0);
160
	if (sd == OPENMSX_INVALID_SOCKET) return 0;
161
	sockaddr_in remote = sock_makeIPv4(0x08080808, 53);
162
	uint32_t ip = 0;
163
	if (connect(sd, std::bit_cast<sockaddr*>(&remote), sizeof(remote)) == 0) {
164
		sockaddr_in local = {};
165
		::socklen_t len = sizeof(local);
166
		if (getsockname(sd, std::bit_cast<sockaddr*>(&local), &len) == 0) {
167
			ip = ntohl(local.sin_addr.s_addr);
168
		}
169
	}
170
	sock_close(sd);
171
	return ip;
172
}
173
174
} // namespace openmsx