SuperTuxKart
stun_detection.hpp
1 //
2 // SuperTuxKart - a fun racing game with go-kart
3 // Copyright (C) 2015 Joerg Henrichs
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 3
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 
19 #ifdef ENABLE_IPV6
20 
21 #ifndef HEADER_STUN_DETECTION
22 #define HEADER_STUN_DETECTION
23 
24 #include "network/socket_address.hpp"
25 #include <atomic>
26 #include <thread>
27 
28 #ifdef WIN32
29 # include <winsock.h>
30 #else
31 # include <sys/socket.h>
32 #endif
33 
34 class StunDetection
35 {
36 private:
38  std::thread m_thread;
39 
41 #ifdef WIN32
42  SOCKET m_socket;
43 #else
44  int m_socket;
45 #endif
46 
48  std::atomic_bool m_connected;
49 
51  std::atomic_bool m_socket_closed;
52 public:
53  // ------------------------------------------------------------------------
54  StunDetection(const std::string& addr, bool ipv4)
55  {
56  m_connected = false;
57  m_socket_closed = true;
58  SocketAddress sa(addr.c_str(), 0/*port specified in addr*/,
59  ipv4 ? AF_INET : AF_INET6);
60  if (sa.isUnset() || (ipv4 && sa.getFamily() != AF_INET) ||
61  (!ipv4 && sa.getFamily() != AF_INET6))
62  return;
63  m_socket = socket(ipv4? AF_INET : AF_INET6, SOCK_STREAM, 0);
64 #ifdef WIN32
65  if (m_socket == INVALID_SOCKET)
66  return;
67 #else
68  if (m_socket == -1)
69  return;
70 #endif
71  m_socket_closed = false;
72  m_thread = std::thread([addr, ipv4, sa, this]()
73  {
74  uint64_t t = StkTime::getMonoTimeMs();
75  if (connect(m_socket, sa.getSockaddr(), sa.getSocklen()) == -1)
76  m_connected.store(false);
77  else
78  m_connected.store(true);
79  shutdown(m_socket, 2);
80 #ifdef WIN32
81  closesocket(m_socket);
82 #else
83  close(m_socket);
84 #endif
85  m_socket_closed.store(true);
86  Log::debug("StunDetection", "Took %dms for %s.",
87  (int)(StkTime::getMonoTimeMs() - t),
88  (addr + (ipv4 ? " (IPv4)" : " (IPv6)")).c_str());
89  });
90  }
91  // ------------------------------------------------------------------------
92  ~StunDetection()
93  {
94  if (m_thread.joinable())
95  {
96  if (m_socket_closed)
97  m_thread.join();
98  else
99  m_thread.detach();
100  }
101  }
102  // ------------------------------------------------------------------------
103  bool isConnecting()
104  { return m_thread.joinable() && m_socket_closed == false; }
105  // ------------------------------------------------------------------------
106  bool connectionSucceeded() { return m_connected == true; }
107  // ------------------------------------------------------------------------
108  bool socketClosed() { return m_socket_closed == true; }
109 };
110 #endif
111 
112 #endif
Describes a IPv4 or IPv6 address in sockaddr_in(6) format, suitable in using with sendto.
Definition: socket_address.hpp:47
static uint64_t getMonoTimeMs()
Returns a time based since the starting of stk (monotonic clock).
Definition: time.hpp:113