forked from catid/libcat
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Sockets.hpp
349 lines (270 loc) · 9.13 KB
/
Sockets.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
/*
Copyright (c) 2009-2013 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_SOCKETS_HPP
#define CAT_SOCKETS_HPP
#include "Platform.hpp"
#if defined(CAT_OS_WINDOWS)
# include <WS2tcpip.h>
#else
# include <unistd.h>
# include <sys/types.h>
# include <netinet/in.h>
#endif
#ifdef CAT_OS_ANDROID
#include <sys/socket.h>
#endif
/*
* This module provides portable socket handles and network address objects
* to simplify creation of IPv4/6 sockets across platforms.
*
* Provides a simple way to set the receive/send buffer sizes.
*
* Provides extra features for UDP sockets: Setting DF bit in header and ICMP
* ignore unreachable.
*
* It does not provide DNS name resolution nor read/write functionality.
*/
namespace cat {
//// Basic definitions
#define CAT_LOOPBACK_IPV4 "127.0.0.1"
#define CAT_LOOPBACK_IPV6 "::1"
typedef u16 Port;
#if defined(CAT_OS_WINDOWS)
typedef SOCKET SocketHandle;
CAT_INLINE bool CloseSocketHandle(SocketHandle s) {
return !closesocket(s);
}
#else
typedef int SocketHandle;
static const SocketHandle INVALID_SOCKET = -1;
static const int SOCKET_ERROR = -1;
CAT_INLINE bool CloseSocketHandle(SocketHandle s) {
return !close(s);
}
#endif
//// Address
#pragma pack(push)
#pragma pack(1)
// Base version of NetAddr that has no ctors so that it can be used in a union
struct CAT_EXPORT UNetAddr {
union {
u8 v6_bytes[16];
u16 v6_words[8];
u64 v6[2];
struct {
u32 v4;
u32 v4_padding[3];
};
} _ip; // Network order
union {
u32 _valid;
struct {
Port _port; // Host order
u16 _family; // Host order
};
};
static const int IP4_BYTES = 4;
static const int IP6_BYTES = 16;
typedef sockaddr_in6 SockAddr;
// These functions are designed to support when this object overlaps the
// memory space of the input
bool Wrap(const sockaddr_in &addr);
bool Wrap(const sockaddr *addr);
CAT_INLINE bool Wrap(const sockaddr_in6 &addr) {
// May be IPv4 that has been stuffed into an IPv6 sockaddr
return Wrap(reinterpret_cast<const sockaddr*>( &addr ));
}
// Promote an IPv4 address to an IPv6 address if needed
bool PromoteTo6();
// Check if an IPv6 address can be demoted to IPv4 address
bool CanDemoteTo4() const;
// Demote an IPv6 address to an IPv4 address if possible,
// otherwise marks address as invalid and returns false
bool DemoteTo4();
CAT_INLINE bool Convert(bool To6) {
if (To6) {
return PromoteTo6();
} else {
return DemoteTo4();
}
}
CAT_INLINE bool Valid() const {
return _valid != 0;
}
CAT_INLINE bool Is6() const {
return _family == AF_INET6;
}
CAT_INLINE const u32 GetIP4() const {
return _ip.v4;
}
CAT_INLINE const u64 *GetIP6() const {
return _ip.v6;
}
CAT_INLINE Port GetPort() const {
return _port;
}
CAT_INLINE void SetPort(Port port) {
_port = port;
}
// Mark the address as invalid
CAT_INLINE void Invalidate() {
_valid = 0;
}
bool EqualsIPOnly(const UNetAddr &addr) const;
CAT_INLINE bool operator==(const UNetAddr &addr) const {
// Check port
if (addr._port != _port) {
return false; // "not equal"
}
// Tail call IP checking function
return EqualsIPOnly(addr);
}
CAT_INLINE bool operator!=(const UNetAddr &addr) const {
return !(*this == addr);
}
// To validate external input; don't want clients connecting
// to their local network instead of the actual game server.
bool IsInternetRoutable();
// Returns true if the address is routable on local network or Internet.
// Returns false if the address is IPv4 multicast, loopback, or weird.
bool IsRoutable();
bool SetFromString(const char *ip_str, Port port = 0);
// Uses the provided buffer to return a string; buffer should be at least 40 chars.
const char *IPToString(char *buffer, int buffer_len) const;
bool SetFromRawIP(const u8 *ip_binary, int bytes);
bool SetFromDotDecimals(int a, int b, int c, int d, Port port = 0);
bool Unwrap(SockAddr &addr, socklen_t &addr_len, bool PromoteToIP6 = false) const;
};
// Wrapper for IPv4 and IPv6 addresses
struct CAT_EXPORT NetAddr : UNetAddr {
CAT_INLINE NetAddr() {
}
CAT_INLINE NetAddr(const char *ip_str, Port port = 0) {
// Invoke SetFromString(), ignoring the return value because
// it will leave the object in an invalid state if needed.
SetFromString(ip_str, port);
}
CAT_INLINE NetAddr(const sockaddr_in6 &addr) {
Wrap(addr);
}
CAT_INLINE NetAddr(const sockaddr_in &addr) {
Wrap(addr);
}
CAT_INLINE NetAddr(const sockaddr *addr) {
Wrap(addr);
}
CAT_INLINE NetAddr(int a, int b, int c, int d, Port port = 0) {
// Invoke SetFromDotDecimals(), ignoring the return value because
// it will leave the object in an invalid state if needed.
SetFromDotDecimals(a, b, c, d, port);
}
CAT_INLINE NetAddr(const NetAddr &addr) {
_valid = addr._valid;
_ip.v6[0] = addr._ip.v6[0];
_ip.v6[1] = addr._ip.v6[1];
}
CAT_INLINE NetAddr &operator=(const NetAddr &addr) {
_valid = addr._valid;
_ip.v6[0] = addr._ip.v6[0];
_ip.v6[1] = addr._ip.v6[1];
return *this;
}
};
#pragma pack(pop)
//// Socket
class CAT_EXPORT Socket {
bool _support4, _support6;
Port _port;
SocketHandle _s;
public:
Socket();
virtual ~Socket();
// The RequestIPv6 flag is a suggestion. Use SupportsIPv6() to check success. The RequireIPv4 flag is always respected.
// NOTE: Socket only supports IPv6 operations after Bind() completes
bool Create(int type, int protocol, bool RequestIPv6 = true, bool RequireIPv4 = true);
// Call these before binding:
bool SetSendBufferSize(int bytes);
bool SetRecvBufferSize(int bytes);
bool Bind(Port port);
// Call these after binding:
CAT_INLINE bool Valid() {
return _s != INVALID_SOCKET;
}
CAT_INLINE bool SupportsIPv4() {
return _support4;
}
CAT_INLINE bool SupportsIPv6() {
return _support6;
}
CAT_INLINE SocketHandle GetSocket() {
return _s;
}
Port GetPort();
void Close();
};
//// UDP Socket
// Adds functions only used for UDP sockets
class CAT_EXPORT UDPSocket : public Socket {
public:
CAT_INLINE virtual ~UDPSocket() {
}
CAT_INLINE bool Create(bool RequestIPv6 = true, bool RequireIPv4 = true) {
return Socket::Create(SOCK_DGRAM, IPPROTO_UDP, RequestIPv6, RequireIPv4);
}
// Call these before binding:
// Disabled by default; ignore ICMP unreachable errors
bool IgnoreUnreachable(bool ignore = true);
// Call these after binding:
// Disabled by default; useful for MTU discovery
bool DontFragment(bool df = true);
};
//// TCP Socket
// Adds functions only used for TCP sockets
class CAT_EXPORT TCPSocket : public Socket {
public:
CAT_INLINE virtual ~TCPSocket() {
}
CAT_INLINE bool Create(bool RequestIPv6 = true, bool RequireIPv4 = true) {
return Socket::Create(SOCK_STREAM, IPPROTO_TCP, RequestIPv6, RequireIPv4);
}
};
// Internal class
class CAT_EXPORT Sockets {
public:
// Call these to initialize the Winsock2 API properly on Windows
static bool OnInitialize();
static void OnFinalize();
// Will unset SupportIPv6 flag if it was unable to support IPv6. Always respects SupportIPv4 flag.
// Always creates an overlapped socket in Windows.
static bool Create(int type, int protocol, bool RequireIPv4, bool &SupportIPv6, SocketHandle &out_s);
static bool AllowIPv4OnIPv6Socket(SocketHandle s);
static bool NetBind(SocketHandle s, Port port, bool SupportIPv6);
static Port GetBoundPort(SocketHandle s);
// Returns a string describing the last error
static const char *GetLastErrorString();
static const char *GetErrorString(int code);
};
} // namespace cat
#endif // CAT_SOCKETS_HPP