Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

358 lines
9.2 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Utility class to help in socket creation. Works for clients + servers
  4. //
  5. //===========================================================================//
  6. #if defined(_WIN32)
  7. #if !defined(_X360)
  8. #include <winsock.h>
  9. #endif
  10. #undef SetPort // winsock screws with the SetPort string... *sigh*
  11. #define socklen_t int
  12. #define MSG_NOSIGNAL 0
  13. #elif POSIX
  14. #include <sys/types.h>
  15. #include <sys/socket.h>
  16. #include <netinet/in.h>
  17. #include <netinet/tcp.h>
  18. #include <errno.h>
  19. #include <sys/ioctl.h>
  20. #define closesocket close
  21. #define WSAGetLastError() errno
  22. #define ioctlsocket ioctl
  23. #ifdef OSX
  24. #define MSG_NOSIGNAL 0
  25. #endif
  26. #endif
  27. #include <tier0/dbg.h>
  28. #include "socketcreator.h"
  29. #include "server.h"
  30. #if defined( _X360 )
  31. #include "xbox/xbox_win32stubs.h"
  32. #endif
  33. // memdbgon must be the last include file in a .cpp file!!!
  34. #include "tier0/memdbgon.h"
  35. bool SocketWouldBlock()
  36. {
  37. #ifdef _WIN32
  38. return (WSAGetLastError() == WSAEWOULDBLOCK);
  39. #elif POSIX
  40. return (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS);
  41. #endif
  42. }
  43. //-----------------------------------------------------------------------------
  44. // Purpose: Constructor
  45. //-----------------------------------------------------------------------------
  46. CSocketCreator::CSocketCreator( ISocketCreatorListener *pListener )
  47. {
  48. m_hListenSocket = -1;
  49. m_pListener = pListener;
  50. }
  51. //-----------------------------------------------------------------------------
  52. // Purpose: Destructor
  53. //-----------------------------------------------------------------------------
  54. CSocketCreator::~CSocketCreator()
  55. {
  56. Disconnect();
  57. }
  58. //-----------------------------------------------------------------------------
  59. // Purpose: returns true if the listening socket is created and listening
  60. //-----------------------------------------------------------------------------
  61. bool CSocketCreator::IsListening() const
  62. {
  63. return m_hListenSocket != -1;
  64. }
  65. //-----------------------------------------------------------------------------
  66. // Purpose: Bind to a TCP port and accept incoming connections
  67. //-----------------------------------------------------------------------------
  68. bool CSocketCreator::CreateListenSocket( const netadr_t &netAdr )
  69. {
  70. CloseListenSocket();
  71. m_ListenAddress = netAdr;
  72. m_hListenSocket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
  73. if ( m_hListenSocket == -1 )
  74. {
  75. Warning( "Socket unable to create socket (%s)\n", NET_ErrorString( WSAGetLastError() ) );
  76. return false;
  77. }
  78. if ( !ConfigureSocket( m_hListenSocket ) )
  79. {
  80. CloseListenSocket();
  81. return false;
  82. }
  83. struct sockaddr_in s;
  84. m_ListenAddress.ToSockadr( (struct sockaddr *)&s );
  85. int ret = bind( m_hListenSocket, (struct sockaddr *)&s, sizeof(struct sockaddr_in) );
  86. if ( ret == -1 )
  87. {
  88. Warning( "Socket bind failed (%s)\n", NET_ErrorString( WSAGetLastError() ) );
  89. CloseListenSocket();
  90. return false;
  91. }
  92. ret = listen( m_hListenSocket, SOCKET_TCP_MAX_ACCEPTS );
  93. if ( ret == -1 )
  94. {
  95. Warning( "Socket listen failed (%s)\n", NET_ErrorString( WSAGetLastError() ) );
  96. CloseListenSocket();
  97. return false;
  98. }
  99. return true;
  100. }
  101. //-----------------------------------------------------------------------------
  102. // Configures a socket for use
  103. //-----------------------------------------------------------------------------
  104. bool CSocketCreator::ConfigureSocket( int sock )
  105. {
  106. // disable NAGLE (rcon cmds are small in size)
  107. int nodelay = 1;
  108. setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&nodelay, sizeof(nodelay));
  109. nodelay = 1;
  110. setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&nodelay, sizeof(nodelay));
  111. int opt = 1, ret;
  112. ret = ioctlsocket( sock, FIONBIO, (unsigned long*)&opt ); // non-blocking
  113. if ( ret == -1 )
  114. {
  115. Warning( "Socket accept ioctl(FIONBIO) failed (%i)\n", WSAGetLastError() );
  116. return false;
  117. }
  118. return true;
  119. }
  120. //-----------------------------------------------------------------------------
  121. // Purpose: Handle a new connection
  122. //-----------------------------------------------------------------------------
  123. void CSocketCreator::ProcessAccept()
  124. {
  125. int newSocket;
  126. sockaddr sa;
  127. int nLengthAddr = sizeof(sa);
  128. newSocket = accept( m_hListenSocket, &sa, (socklen_t *)&nLengthAddr );
  129. if ( newSocket == -1 )
  130. {
  131. if ( !SocketWouldBlock()
  132. #ifdef POSIX
  133. && errno != EINTR
  134. #endif
  135. )
  136. {
  137. Warning ("Socket ProcessAccept Error: %s\n", NET_ErrorString( WSAGetLastError() ) );
  138. }
  139. return;
  140. }
  141. if ( !ConfigureSocket( newSocket ) )
  142. {
  143. closesocket( newSocket );
  144. return;
  145. }
  146. netadr_t adr;
  147. adr.SetFromSockadr( &sa );
  148. if ( m_pListener && !m_pListener->ShouldAcceptSocket( newSocket, adr ) )
  149. {
  150. closesocket( newSocket );
  151. return;
  152. }
  153. // new connection TCP request, put in accepted queue
  154. int nIndex = m_hAcceptedSockets.AddToTail();
  155. AcceptedSocket_t *pNewEntry = &m_hAcceptedSockets[nIndex];
  156. pNewEntry->m_hSocket = newSocket;
  157. pNewEntry->m_Address = adr;
  158. pNewEntry->m_pData = NULL;
  159. void* pData = NULL;
  160. if ( m_pListener )
  161. {
  162. m_pListener->OnSocketAccepted( newSocket, adr, &pData );
  163. }
  164. pNewEntry->m_pData = pData;
  165. }
  166. //-----------------------------------------------------------------------------
  167. // Purpose: connect to the remote server
  168. //-----------------------------------------------------------------------------
  169. int CSocketCreator::ConnectSocket( const netadr_t &netAdr, bool bSingleSocket )
  170. {
  171. if ( bSingleSocket )
  172. {
  173. CloseAllAcceptedSockets();
  174. }
  175. SocketHandle_t hSocket = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP );
  176. if ( hSocket == -1 )
  177. {
  178. Warning( "Unable to create socket (%s)\n", NET_ErrorString( WSAGetLastError() ) );
  179. return -1;
  180. }
  181. int opt = 1, ret;
  182. ret = ioctlsocket( hSocket, FIONBIO, (unsigned long*)&opt ); // non-blocking
  183. if ( ret == -1 )
  184. {
  185. Warning( "Socket ioctl(FIONBIO) failed (%s)\n", NET_ErrorString( WSAGetLastError() ) );
  186. closesocket( hSocket );
  187. return -1;
  188. }
  189. // disable NAGLE (rcon cmds are small in size)
  190. int nodelay = 1;
  191. setsockopt( hSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&nodelay, sizeof(nodelay) );
  192. struct sockaddr_in s;
  193. netAdr.ToSockadr( (struct sockaddr *)&s );
  194. ret = connect( hSocket, (struct sockaddr *)&s, sizeof(s));
  195. if ( ret == -1 )
  196. {
  197. if ( !SocketWouldBlock() )
  198. {
  199. Warning( "Socket connection failed (%s)\n", NET_ErrorString( WSAGetLastError() ) );
  200. closesocket( hSocket );
  201. return -1;
  202. }
  203. fd_set writefds;
  204. struct timeval tv;
  205. tv.tv_usec = 0;
  206. tv.tv_sec = 1;
  207. FD_ZERO( &writefds );
  208. FD_SET( static_cast<u_int>( hSocket ), &writefds );
  209. if ( select ( hSocket + 1, NULL, &writefds, NULL, &tv ) < 1 ) // block for at most 1 second
  210. {
  211. closesocket( hSocket ); // took too long to connect to, give up
  212. return -1;
  213. }
  214. }
  215. if ( m_pListener && !m_pListener->ShouldAcceptSocket( hSocket, netAdr ) )
  216. {
  217. closesocket( hSocket );
  218. return -1;
  219. }
  220. // new connection TCP request, put in accepted queue
  221. void *pData = NULL;
  222. int nIndex = m_hAcceptedSockets.AddToTail();
  223. AcceptedSocket_t *pNewEntry = &m_hAcceptedSockets[nIndex];
  224. pNewEntry->m_hSocket = hSocket;
  225. pNewEntry->m_Address = netAdr;
  226. pNewEntry->m_pData = NULL;
  227. if ( m_pListener )
  228. {
  229. m_pListener->OnSocketAccepted( hSocket, netAdr, &pData );
  230. }
  231. pNewEntry->m_pData = pData;
  232. return nIndex;
  233. }
  234. //-----------------------------------------------------------------------------
  235. // Purpose: close an open rcon connection
  236. //-----------------------------------------------------------------------------
  237. void CSocketCreator::CloseListenSocket()
  238. {
  239. if ( m_hListenSocket != -1 )
  240. {
  241. closesocket( m_hListenSocket );
  242. m_hListenSocket = -1;
  243. }
  244. }
  245. void CSocketCreator::CloseAcceptedSocket( int nIndex )
  246. {
  247. if ( nIndex >= m_hAcceptedSockets.Count() )
  248. return;
  249. AcceptedSocket_t& connected = m_hAcceptedSockets[nIndex];
  250. if ( m_pListener )
  251. {
  252. m_pListener->OnSocketClosed( connected.m_hSocket, connected.m_Address, connected.m_pData );
  253. }
  254. closesocket( connected.m_hSocket );
  255. m_hAcceptedSockets.Remove( nIndex );
  256. }
  257. void CSocketCreator::CloseAllAcceptedSockets()
  258. {
  259. int nCount = m_hAcceptedSockets.Count();
  260. for ( int i = 0; i < nCount; ++i )
  261. {
  262. AcceptedSocket_t& connected = m_hAcceptedSockets[i];
  263. if ( m_pListener )
  264. {
  265. m_pListener->OnSocketClosed( connected.m_hSocket, connected.m_Address, connected.m_pData );
  266. }
  267. closesocket( connected.m_hSocket );
  268. }
  269. m_hAcceptedSockets.RemoveAll();
  270. }
  271. void CSocketCreator::Disconnect()
  272. {
  273. CloseListenSocket();
  274. CloseAllAcceptedSockets();
  275. }
  276. //-----------------------------------------------------------------------------
  277. // Purpose: accept new connections and walk open sockets and handle any incoming data
  278. //-----------------------------------------------------------------------------
  279. void CSocketCreator::RunFrame()
  280. {
  281. if ( IsListening() )
  282. {
  283. ProcessAccept(); // handle any new connection requests
  284. }
  285. }
  286. //-----------------------------------------------------------------------------
  287. // Returns socket info
  288. //-----------------------------------------------------------------------------
  289. int CSocketCreator::GetAcceptedSocketCount() const
  290. {
  291. return m_hAcceptedSockets.Count();
  292. }
  293. SocketHandle_t CSocketCreator::GetAcceptedSocketHandle( int nIndex ) const
  294. {
  295. return m_hAcceptedSockets[nIndex].m_hSocket;
  296. }
  297. const netadr_t& CSocketCreator::GetAcceptedSocketAddress( int nIndex ) const
  298. {
  299. return m_hAcceptedSockets[nIndex].m_Address;
  300. }
  301. void* CSocketCreator::GetAcceptedSocketData( int nIndex )
  302. {
  303. return m_hAcceptedSockets[nIndex].m_pData;
  304. }