Cuberite
A lightweight, fast and extensible game server for Minecraft
ServerHandleImpl.cpp
Go to the documentation of this file.
1 
2 // ServerHandleImpl.cpp
3 
4 // Implements the cServerHandleImpl class implementing the TCP server functionality
5 
6 #include "Globals.h"
7 #include "ServerHandleImpl.h"
8 #include "TCPLinkImpl.h"
9 #include "NetworkSingleton.h"
10 
11 
12 
13 
14 
16 // Globals:
17 
19 {
20  static bool IsValidSocket(evutil_socket_t a_Socket)
21  {
22 #ifdef _WIN32
23  return (a_Socket != INVALID_SOCKET);
24 #else // _WIN32
25  return (a_Socket >= 0);
26 #endif // else _WIN32
27  }
28 }
29 
30 
31 
32 
33 
35 // cServerHandleImpl:
36 
38  m_ListenCallbacks(std::move(a_ListenCallbacks)),
39  m_ConnListener(nullptr),
40  m_SecondaryConnListener(nullptr),
41  m_IsListening(false),
42  m_ErrorCode(0)
43 {
44 }
45 
46 
47 
48 
49 
51 {
52  if (m_ConnListener != nullptr)
53  {
54  evconnlistener_free(m_ConnListener);
55  }
56  if (m_SecondaryConnListener != nullptr)
57  {
58  evconnlistener_free(m_SecondaryConnListener);
59  }
60 }
61 
62 
63 
64 
65 
67 {
68  // Stop the listener sockets:
69  if (m_ConnListener != nullptr)
70  {
71  evconnlistener_disable(m_ConnListener);
72  }
73  if (m_SecondaryConnListener != nullptr)
74  {
75  evconnlistener_disable(m_SecondaryConnListener);
76  }
77  m_IsListening = false;
78 
79  // Shutdown all connections:
80  cTCPLinkImplPtrs Conns;
81  {
82  cCSLock Lock(m_CS);
83  std::swap(Conns, m_Connections);
84  }
85  for (const auto & conn: Conns)
86  {
87  conn->Shutdown();
88  }
89 
90  // Remove the ptr to self, so that the object may be freed:
91  m_SelfPtr.reset();
92 
93  // Remove self from cNetworkSingleton:
95 }
96 
97 
98 
99 
100 
102  UInt16 a_Port,
103  cNetwork::cListenCallbacksPtr a_ListenCallbacks
104 )
105 {
106  cServerHandleImplPtr res{new cServerHandleImpl(std::move(a_ListenCallbacks))};
107  res->m_SelfPtr = res;
108  if (res->Listen(a_Port))
109  {
111  }
112  else
113  {
114  res->m_ListenCallbacks->OnError(res->m_ErrorCode, res->m_ErrorMsg);
115  res->m_SelfPtr.reset();
116  }
117  return res;
118 }
119 
120 
121 
122 
123 
125 {
126  // Make sure the cNetwork internals are innitialized:
128 
129  // Set up the main socket:
130  // It should listen on IPv6 with IPv4 fallback, when available; IPv4 when IPv6 is not available.
131  bool NeedsTwoSockets = false;
132  int err = 0;
133  evutil_socket_t MainSock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
134 
136  {
137  // Failed to create IPv6 socket, create an IPv4 one instead:
138  err = EVUTIL_SOCKET_ERROR();
139  LOGD("Failed to create IPv6 MainSock: %d (%s)", err, evutil_socket_error_to_string(err));
140  MainSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
142  {
143  m_ErrorCode = EVUTIL_SOCKET_ERROR();
144  m_ErrorMsg = fmt::format(FMT_STRING("Cannot create a server socket for port {}: {} ({})"),
145  a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode)
146  );
147  return false;
148  }
149 
150  // Allow the port to be reused right after the socket closes:
151  if (evutil_make_listen_socket_reuseable(MainSock) != 0)
152  {
153  m_ErrorCode = EVUTIL_SOCKET_ERROR();
154  m_ErrorMsg = fmt::format(FMT_STRING("Port {} cannot be made reusable: {} ({}). Restarting the server might not work."),
155  a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode)
156  );
157  LOG("%s", m_ErrorMsg);
158  }
159 
160  // Bind to all interfaces:
161  sockaddr_in name;
162  memset(&name, 0, sizeof(name));
163  name.sin_family = AF_INET;
164  name.sin_port = ntohs(a_Port);
165  if (bind(MainSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
166  {
167  m_ErrorCode = EVUTIL_SOCKET_ERROR();
168  m_ErrorMsg = fmt::format(FMT_STRING("Cannot bind IPv4 socket to port {}: {} ({})"),
169  a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode)
170  );
171  evutil_closesocket(MainSock);
172  return false;
173  }
174  }
175  else
176  {
177  // IPv6 socket created, switch it into "dualstack" mode:
178  UInt32 Zero = 0;
179  #ifdef _WIN32
180  // WinXP doesn't support this feature, so if the setting fails, create another socket later on:
181  int res = setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&Zero), sizeof(Zero));
182  err = EVUTIL_SOCKET_ERROR();
183  NeedsTwoSockets = ((res == SOCKET_ERROR) && (err == WSAENOPROTOOPT));
184  #else
185  setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&Zero), sizeof(Zero));
186  #endif
187 
188  // Allow the port to be reused right after the socket closes:
189  if (evutil_make_listen_socket_reuseable(MainSock) != 0)
190  {
191  m_ErrorCode = EVUTIL_SOCKET_ERROR();
192  m_ErrorMsg = fmt::format(FMT_STRING("Port {} cannot be made reusable: {} ({}). Restarting the server might not work."),
193  a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode)
194  );
195  LOG("%s", m_ErrorMsg);
196  }
197 
198  // Bind to all interfaces:
199  sockaddr_in6 name;
200  memset(&name, 0, sizeof(name));
201  name.sin6_family = AF_INET6;
202  name.sin6_port = ntohs(a_Port);
203  if (bind(MainSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
204  {
205  m_ErrorCode = EVUTIL_SOCKET_ERROR();
206  m_ErrorMsg = fmt::format(FMT_STRING("Cannot bind IPv6 socket to port {}: {} ({})"), a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode));
207  evutil_closesocket(MainSock);
208  return false;
209  }
210  }
211  if (evutil_make_socket_nonblocking(MainSock) != 0)
212  {
213  m_ErrorCode = EVUTIL_SOCKET_ERROR();
214  m_ErrorMsg = fmt::format(FMT_STRING("Cannot make socket on port {} non-blocking: {} ({})"), a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode));
215  evutil_closesocket(MainSock);
216  return false;
217  }
218  if (listen(MainSock, SOMAXCONN) != 0)
219  {
220  m_ErrorCode = EVUTIL_SOCKET_ERROR();
221  m_ErrorMsg = fmt::format(FMT_STRING("Cannot listen on port {}: {} ({})"), a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode));
222  evutil_closesocket(MainSock);
223  return false;
224  }
225  m_ConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, MainSock);
226  m_IsListening = true;
227 
228  if (!NeedsTwoSockets)
229  {
230  return true;
231  }
232 
233  // If a secondary socket is required (WinXP dual-stack), create it here:
234  LOGD("Creating a second socket for IPv4");
235  evutil_socket_t SecondSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
236 
237  if (!ServerHandleImplHelper::IsValidSocket(SecondSock))
238  {
239  err = EVUTIL_SOCKET_ERROR();
240  LOGD("socket(AF_INET, ...) failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err));
241  return true; // Report as success, the primary socket is working
242  }
243 
244  // Allow the port to be reused right after the socket closes:
245  if (evutil_make_listen_socket_reuseable(SecondSock) != 0)
246  {
247  m_ErrorCode = EVUTIL_SOCKET_ERROR();
248  m_ErrorMsg = fmt::format(FMT_STRING("Port {} cannot be made reusable (second socket): {} ({}). Restarting the server might not work."),
249  a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode)
250  );
251  LOG("%s", m_ErrorMsg);
252  }
253 
254  // Make the secondary socket nonblocking:
255  if (evutil_make_socket_nonblocking(SecondSock) != 0)
256  {
257  err = EVUTIL_SOCKET_ERROR();
258  LOGD("evutil_make_socket_nonblocking() failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err));
259  evutil_closesocket(SecondSock);
260  return true; // Report as success, the primary socket is working
261  }
262 
263  // Bind to all IPv4 interfaces:
264  sockaddr_in name;
265  memset(&name, 0, sizeof(name));
266  name.sin_family = AF_INET;
267  name.sin_port = ntohs(a_Port);
268  if (bind(SecondSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
269  {
270  err = EVUTIL_SOCKET_ERROR();
271  LOGD("Cannot bind secondary socket to port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err));
272  evutil_closesocket(SecondSock);
273  return true; // Report as success, the primary socket is working
274  }
275 
276  if (listen(SecondSock, SOMAXCONN) != 0)
277  {
278  err = EVUTIL_SOCKET_ERROR();
279  LOGD("Cannot listen on secondary socket on port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err));
280  evutil_closesocket(SecondSock);
281  return true; // Report as success, the primary socket is working
282  }
283 
284  UNUSED(err);
285 
286  m_SecondaryConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, SecondSock);
287  return true;
288 }
289 
290 
291 
292 
293 
294 void cServerHandleImpl::Callback(evconnlistener * a_Listener, evutil_socket_t a_Socket, sockaddr * a_Addr, int a_Len, void * a_Self)
295 {
296  // Cast to true self:
297  cServerHandleImpl * Self = static_cast<cServerHandleImpl *>(a_Self);
298  ASSERT(Self != nullptr);
299  ASSERT(Self->m_SelfPtr != nullptr);
300 
301  // Get the textual IP address and port number out of a_Addr:
302  char IPAddress[128];
303  UInt16 Port = 0;
304  switch (a_Addr->sa_family)
305  {
306  case AF_INET:
307  {
308  sockaddr_in * sin = reinterpret_cast<sockaddr_in *>(a_Addr);
309  evutil_inet_ntop(AF_INET, &(sin->sin_addr), IPAddress, ARRAYCOUNT(IPAddress));
310  Port = ntohs(sin->sin_port);
311  break;
312  }
313  case AF_INET6:
314  {
315  sockaddr_in6 * sin6 = reinterpret_cast<sockaddr_in6 *>(a_Addr);
316  evutil_inet_ntop(AF_INET6, &(sin6->sin6_addr), IPAddress, ARRAYCOUNT(IPAddress));
317  Port = ntohs(sin6->sin6_port);
318  break;
319  }
320  }
321 
322  // Call the OnIncomingConnection callback to get the link callbacks to use:
323  cTCPLink::cCallbacksPtr LinkCallbacks = Self->m_ListenCallbacks->OnIncomingConnection(IPAddress, Port);
324  if (LinkCallbacks == nullptr)
325  {
326  // Drop the connection:
327  evutil_closesocket(a_Socket);
328  return;
329  }
330 
331  const int one = 1;
332  setsockopt(a_Socket, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<const char *>(&one), sizeof(one));
333 
334  // Create a new cTCPLink for the incoming connection:
335  cTCPLinkImplPtr Link = std::make_shared<cTCPLinkImpl>(a_Socket, LinkCallbacks, Self->m_SelfPtr, a_Addr, static_cast<socklen_t>(a_Len));
336  {
337  cCSLock Lock(Self->m_CS);
338  Self->m_Connections.push_back(Link);
339  } // Lock(m_CS)
340  LinkCallbacks->OnLinkCreated(Link);
341  Link->Enable(Link);
342 
343  // Call the OnAccepted callback:
344  Self->m_ListenCallbacks->OnAccepted(*Link);
345 }
346 
347 
348 
349 
350 
352 {
353  cCSLock Lock(m_CS);
354  for (auto itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr)
355  {
356  if (itr->get() == a_Link)
357  {
358  m_Connections.erase(itr);
359  return;
360  }
361  } // for itr - m_Connections[]
362 }
363 
364 
365 
366 
367 
369 // cNetwork API:
370 
372  UInt16 a_Port,
373  cNetwork::cListenCallbacksPtr a_ListenCallbacks
374 )
375 {
376  return cServerHandleImpl::Listen(a_Port, std::move(a_ListenCallbacks));
377 }
378 
379 
380 
381 
382 
#define ARRAYCOUNT(X)
Evaluates to the number of elements in an array (compile-time!)
Definition: Globals.h:231
unsigned int UInt32
Definition: Globals.h:157
#define ASSERT(x)
Definition: Globals.h:276
unsigned short UInt16
Definition: Globals.h:158
#define UNUSED
Definition: Globals.h:72
void LOG(std::string_view a_Format, const Args &... args)
Definition: LoggerSimple.h:55
#define LOGD
Definition: LoggerSimple.h:83
std::shared_ptr< cServerHandle > cServerHandlePtr
Definition: Network.h:28
std::shared_ptr< cTCPLinkImpl > cTCPLinkImplPtr
std::vector< cTCPLinkImplPtr > cTCPLinkImplPtrs
std::shared_ptr< cServerHandleImpl > cServerHandleImplPtr
static bool IsValidSocket(evutil_socket_t a_Socket)
Definition: FastNBT.h:132
RAII for cCriticalSection - locks the CS on creation, unlocks on destruction.
std::shared_ptr< cCallbacks > cCallbacksPtr
Definition: Network.h:71
static cServerHandlePtr Listen(UInt16 a_Port, cListenCallbacksPtr a_ListenCallbacks)
Opens up the specified port for incoming connections.
std::shared_ptr< cListenCallbacks > cListenCallbacksPtr
Definition: Network.h:273
static cNetworkSingleton & Get(void)
Returns the singleton instance of this class.
void AddServer(const cServerHandlePtr &a_Server)
Adds the specified link to m_Servers.
void RemoveServer(const cServerHandle *a_Server)
Removes the specified server from m_Servers.
cServerHandleImpl(cNetwork::cListenCallbacksPtr a_ListenCallbacks)
Creates a new instance with the specified callbacks.
evconnlistener * m_ConnListener
The LibEvent handle representing the main listening socket.
static void Callback(evconnlistener *a_Listener, evutil_socket_t a_Socket, sockaddr *a_Addr, int a_Len, void *a_Self)
The callback called by LibEvent upon incoming connection.
evconnlistener * m_SecondaryConnListener
The LibEvent handle representing the secondary listening socket (only when side-by-side listening is ...
AString m_ErrorMsg
Contains the error message for the failure to listen.
cServerHandleImplPtr m_SelfPtr
The SharedPtr to self, so that it can be passed to created links.
cTCPLinkImplPtrs m_Connections
Container for all currently active connections on this server.
cCriticalSection m_CS
Mutex protecting m_Connections againt multithreaded access.
virtual void Close(void) override
Stops the server, no more incoming connections will be accepted.
static cServerHandleImplPtr Listen(UInt16 a_Port, cNetwork::cListenCallbacksPtr a_ListenCallbacks)
Creates a new server instance listening on the specified port.
virtual ~cServerHandleImpl() override
Closes the server, dropping all the connections.
bool m_IsListening
Set to true when the server is initialized successfully and is listening for incoming connections.
int m_ErrorCode
Contains the error code for the failure to listen.
cNetwork::cListenCallbacksPtr m_ListenCallbacks
The callbacks used to notify about incoming connections.
void RemoveLink(const cTCPLinkImpl *a_Link)
Removes the specified link from m_Connections.