PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/xbmc/network/Socket.cpp

http://github.com/xbmc/xbmc
C++ | 328 lines | 255 code | 43 blank | 30 comment | 44 complexity | 771328ef2f714166c08064502e24f138 MD5 | raw file
Possible License(s): GPL-3.0, CC-BY-SA-3.0, LGPL-2.0, 0BSD, Unlicense, GPL-2.0, AGPL-1.0, BSD-3-Clause, LGPL-2.1, LGPL-3.0
  1. /*
  2. * Socket classes
  3. * Copyright (c) 2008 d4rk
  4. * Copyright (C) 2008-2018 Team Kodi
  5. * This file is part of Kodi - https://kodi.tv
  6. *
  7. * SPDX-License-Identifier: GPL-2.0-or-later
  8. * See LICENSES/README.md for more information.
  9. */
  10. #include "Socket.h"
  11. #include "utils/ScopeGuard.h"
  12. #include "utils/log.h"
  13. #include <vector>
  14. using namespace SOCKETS;
  15. #ifdef WINSOCK_VERSION
  16. #define close closesocket
  17. #endif
  18. /**********************************************************************/
  19. /* CPosixUDPSocket */
  20. /**********************************************************************/
  21. bool CPosixUDPSocket::Bind(bool localOnly, int port, int range)
  22. {
  23. // close any existing sockets
  24. Close();
  25. // If we can, create a socket that works with IPv6 and IPv4.
  26. // If not, try an IPv4-only socket (we don't want to end up
  27. // with an IPv6-only socket).
  28. if (!localOnly) // Only bind loopback to ipv4. TODO : Implement dual bindinds.
  29. {
  30. m_ipv6Socket = CheckIPv6(port, range);
  31. if (m_ipv6Socket)
  32. {
  33. m_iSock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
  34. if (m_iSock != INVALID_SOCKET)
  35. {
  36. #ifdef WINSOCK_VERSION
  37. const char zero = 0;
  38. #else
  39. int zero = 0;
  40. #endif
  41. if (setsockopt(m_iSock, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)) == -1)
  42. {
  43. closesocket(m_iSock);
  44. m_iSock = INVALID_SOCKET;
  45. }
  46. }
  47. }
  48. }
  49. if (m_iSock == INVALID_SOCKET)
  50. m_iSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  51. if (m_iSock == INVALID_SOCKET)
  52. {
  53. #ifdef TARGET_WINDOWS
  54. int ierr = WSAGetLastError();
  55. CLog::Log(LOGERROR, "UDP: Could not create socket %d", ierr);
  56. // hack for broken third party libs
  57. if (ierr == WSANOTINITIALISED)
  58. {
  59. WSADATA wd;
  60. if (WSAStartup(MAKEWORD(2,2), &wd) != 0)
  61. CLog::Log(LOGERROR, "UDP: WSAStartup failed");
  62. }
  63. #else
  64. CLog::Log(LOGERROR, "UDP: Could not create socket");
  65. #endif
  66. CLog::Log(LOGERROR, "UDP: %s", strerror(errno));
  67. return false;
  68. }
  69. // make sure we can reuse the address
  70. #ifdef WINSOCK_VERSION
  71. const char yes=1;
  72. #else
  73. int yes = 1;
  74. #endif
  75. if (setsockopt(m_iSock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1)
  76. {
  77. CLog::Log(LOGWARNING, "UDP: Could not enable the address reuse options");
  78. CLog::Log(LOGWARNING, "UDP: %s", strerror(errno));
  79. }
  80. // bind to any address or localhost
  81. if (m_ipv6Socket)
  82. {
  83. if (localOnly)
  84. m_addr = CAddress("::1");
  85. else
  86. m_addr = CAddress("::");
  87. }
  88. else
  89. {
  90. if (localOnly)
  91. m_addr = CAddress("127.0.0.1");
  92. else
  93. m_addr = CAddress("0.0.0.0");
  94. }
  95. // bind the socket ( try from port to port+range )
  96. for (m_iPort = port; m_iPort <= port + range; ++m_iPort)
  97. {
  98. if (m_ipv6Socket)
  99. m_addr.saddr.saddr6.sin6_port = htons(m_iPort);
  100. else
  101. m_addr.saddr.saddr4.sin_port = htons(m_iPort);
  102. if (bind(m_iSock, (struct sockaddr*)&m_addr.saddr, m_addr.size) != 0)
  103. {
  104. CLog::Log(LOGWARNING, "UDP: Error binding socket on port %d (ipv6 : %s)", m_iPort, m_ipv6Socket ? "true" : "false" );
  105. CLog::Log(LOGWARNING, "UDP: %s", strerror(errno));
  106. }
  107. else
  108. {
  109. CLog::Log(LOGINFO, "UDP: Listening on port %d (ipv6 : %s)", m_iPort,
  110. m_ipv6Socket ? "true" : "false");
  111. SetBound();
  112. SetReady();
  113. break;
  114. }
  115. }
  116. // check for errors
  117. if (!Bound())
  118. {
  119. CLog::Log(LOGERROR, "UDP: No suitable port found");
  120. Close();
  121. return false;
  122. }
  123. return true;
  124. }
  125. bool CPosixUDPSocket::CheckIPv6(int port, int range)
  126. {
  127. CAddress testaddr("::");
  128. #if defined(TARGET_WINDOWS)
  129. using CAutoPtrSocket = KODI::UTILS::CScopeGuard<SOCKET, INVALID_SOCKET, decltype(closesocket)>;
  130. CAutoPtrSocket testSocket(closesocket, socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP));
  131. #else
  132. using CAutoPtrSocket = KODI::UTILS::CScopeGuard<int, -1, decltype(close)>;
  133. CAutoPtrSocket testSocket(close, socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP));
  134. #endif
  135. if (static_cast<SOCKET>(testSocket) == INVALID_SOCKET)
  136. {
  137. CLog::LogF(LOGDEBUG, "Could not create IPv6 socket: %s", strerror(errno));
  138. return false;
  139. }
  140. #ifdef WINSOCK_VERSION
  141. const char zero = 0;
  142. #else
  143. int zero = 0;
  144. #endif
  145. if (setsockopt(static_cast<SOCKET>(testSocket), IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)) ==
  146. -1)
  147. {
  148. CLog::LogF(LOGDEBUG, "Could not disable IPV6_V6ONLY for socket: %s", strerror(errno));
  149. return false;
  150. }
  151. // Try to bind a socket to validate ipv6 status
  152. for (; port <= port + range; port++)
  153. {
  154. testaddr.saddr.saddr6.sin6_port = htons(port);
  155. if (bind(static_cast<SOCKET>(testSocket), reinterpret_cast<struct sockaddr*>(&testaddr.saddr),
  156. testaddr.size) == 0)
  157. {
  158. CLog::LogF(LOGDEBUG, "IPv6 socket bound successfully");
  159. return true;
  160. }
  161. else
  162. {
  163. CLog::LogF(LOGDEBUG, "Could not bind IPv6 socket: %s", strerror(errno));
  164. }
  165. }
  166. return false;
  167. }
  168. void CPosixUDPSocket::Close()
  169. {
  170. if (m_iSock>=0)
  171. {
  172. close(m_iSock);
  173. m_iSock = INVALID_SOCKET;
  174. }
  175. SetBound(false);
  176. SetReady(false);
  177. }
  178. int CPosixUDPSocket::Read(CAddress& addr, const int buffersize, void *buffer)
  179. {
  180. if (m_ipv6Socket)
  181. addr.SetAddress("::");
  182. return (int)recvfrom(m_iSock, (char*)buffer, (size_t)buffersize, 0,
  183. (struct sockaddr*)&addr.saddr, &addr.size);
  184. }
  185. int CPosixUDPSocket::SendTo(const CAddress& addr, const int buffersize,
  186. const void *buffer)
  187. {
  188. return (int)sendto(m_iSock, (const char *)buffer, (size_t)buffersize, 0,
  189. (const struct sockaddr*)&addr.saddr, addr.size);
  190. }
  191. /**********************************************************************/
  192. /* CSocketFactory */
  193. /**********************************************************************/
  194. CUDPSocket* CSocketFactory::CreateUDPSocket()
  195. {
  196. return new CPosixUDPSocket();
  197. }
  198. /**********************************************************************/
  199. /* CSocketListener */
  200. /**********************************************************************/
  201. CSocketListener::CSocketListener()
  202. {
  203. Clear();
  204. }
  205. void CSocketListener::AddSocket(CBaseSocket *sock)
  206. {
  207. // WARNING: not threadsafe (which is ok for now)
  208. if (sock && sock->Ready())
  209. {
  210. m_sockets.push_back(sock);
  211. FD_SET(sock->Socket(), &m_fdset);
  212. #ifndef WINSOCK_VERSION
  213. if (sock->Socket() > m_iMaxSockets)
  214. m_iMaxSockets = sock->Socket();
  215. #endif
  216. }
  217. }
  218. bool CSocketListener::Listen(int timeout)
  219. {
  220. if (m_sockets.size()==0)
  221. {
  222. CLog::Log(LOGERROR, "SOCK: No sockets to listen for");
  223. throw LISTENEMPTY;
  224. }
  225. m_iReadyCount = 0;
  226. m_iCurrentSocket = 0;
  227. FD_ZERO(&m_fdset);
  228. for (unsigned int i = 0 ; i<m_sockets.size() ; i++)
  229. {
  230. FD_SET(m_sockets[i]->Socket(), &m_fdset);
  231. }
  232. // set our timeout
  233. struct timeval tv;
  234. int rem = timeout % 1000;
  235. tv.tv_usec = rem * 1000;
  236. tv.tv_sec = timeout / 1000;
  237. m_iReadyCount = select(m_iMaxSockets+1, &m_fdset, NULL, NULL, (timeout < 0 ? NULL : &tv));
  238. if (m_iReadyCount<0)
  239. {
  240. CLog::Log(LOGERROR, "SOCK: Error selecting socket(s)");
  241. Clear();
  242. throw LISTENERROR;
  243. }
  244. else
  245. {
  246. m_iCurrentSocket = 0;
  247. return (m_iReadyCount>0);
  248. }
  249. }
  250. void CSocketListener::Clear()
  251. {
  252. m_sockets.clear();
  253. FD_ZERO(&m_fdset);
  254. m_iReadyCount = 0;
  255. m_iMaxSockets = 0;
  256. m_iCurrentSocket = 0;
  257. }
  258. CBaseSocket* CSocketListener::GetFirstReadySocket()
  259. {
  260. if (m_iReadyCount<=0)
  261. return NULL;
  262. for (int i = 0 ; i < (int)m_sockets.size() ; i++)
  263. {
  264. if (FD_ISSET((m_sockets[i])->Socket(), &m_fdset))
  265. {
  266. m_iCurrentSocket = i;
  267. return m_sockets[i];
  268. }
  269. }
  270. return NULL;
  271. }
  272. CBaseSocket* CSocketListener::GetNextReadySocket()
  273. {
  274. if (m_iReadyCount<=0)
  275. return NULL;
  276. for (int i = m_iCurrentSocket+1 ; i<(int)m_sockets.size() ; i++)
  277. {
  278. if (FD_ISSET(m_sockets[i]->Socket(), &m_fdset))
  279. {
  280. m_iCurrentSocket = i;
  281. return m_sockets[i];
  282. }
  283. }
  284. return NULL;
  285. }