icetransport.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. /**
  2. * Copyright (c) 2019 Paul-Louis Ageneau
  3. *
  4. * This library is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Lesser General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 2.1 of the License, or (at your option) any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * Lesser General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser General Public
  15. * License along with this library; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. */
  18. #include "icetransport.hpp"
  19. #include <netdb.h>
  20. #include <netinet/in.h>
  21. #include <sys/socket.h>
  22. #include <sys/types.h>
  23. #include <chrono>
  24. #include <iostream>
  25. #include <random>
  26. #include <sstream>
  27. namespace rtc {
  28. using std::shared_ptr;
  29. using std::weak_ptr;
  30. IceTransport::IceTransport(const Configuration &config, Description::Role role,
  31. candidate_callback candidateCallback,
  32. state_callback stateChangeCallback,
  33. gathering_state_callback gatheringStateChangeCallback)
  34. : mRole(role), mMid("0"), mState(State::Disconnected), mGatheringState(GatheringState::New),
  35. mNiceAgent(nullptr, nullptr), mMainLoop(nullptr, nullptr),
  36. mCandidateCallback(std::move(candidateCallback)),
  37. mStateChangeCallback(std::move(stateChangeCallback)),
  38. mGatheringStateChangeCallback(std::move(gatheringStateChangeCallback)) {
  39. auto logLevelFlags = GLogLevelFlags(G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION);
  40. g_log_set_handler(nullptr, logLevelFlags, LogCallback, this);
  41. nice_debug_enable(false);
  42. mMainLoop = decltype(mMainLoop)(g_main_loop_new(nullptr, FALSE), g_main_loop_unref);
  43. if (!mMainLoop)
  44. std::runtime_error("Failed to create the main loop");
  45. mNiceAgent = decltype(mNiceAgent)(
  46. nice_agent_new(g_main_loop_get_context(mMainLoop.get()), NICE_COMPATIBILITY_RFC5245),
  47. g_object_unref);
  48. if (!mNiceAgent)
  49. throw std::runtime_error("Failed to create the nice agent");
  50. mMainLoopThread = std::thread(g_main_loop_run, mMainLoop.get());
  51. g_object_set(G_OBJECT(mNiceAgent.get()), "controlling-mode", TRUE, nullptr);
  52. g_object_set(G_OBJECT(mNiceAgent.get()), "ice-udp", TRUE, nullptr);
  53. g_object_set(G_OBJECT(mNiceAgent.get()), "ice-tcp", FALSE, nullptr);
  54. g_object_set(G_OBJECT(mNiceAgent.get()), "stun-initial-timeout", 200, nullptr);
  55. g_object_set(G_OBJECT(mNiceAgent.get()), "stun-max-retransmissions", 3, nullptr);
  56. g_object_set(G_OBJECT(mNiceAgent.get()), "stun-pacing-timer", 20, nullptr);
  57. g_object_set(G_OBJECT(mNiceAgent.get()), "upnp", FALSE, nullptr);
  58. g_object_set(G_OBJECT(mNiceAgent.get()), "upnp-timeout", 200, nullptr);
  59. std::vector<IceServer> servers = config.iceServers;
  60. unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
  61. std::shuffle(servers.begin(), servers.end(), std::default_random_engine(seed));
  62. bool success = false;
  63. for (auto &server : servers) {
  64. if (server.hostname.empty())
  65. continue;
  66. if (server.service.empty())
  67. server.service = "3478"; // STUN UDP port
  68. struct addrinfo hints = {};
  69. hints.ai_family = AF_INET; // IPv4
  70. hints.ai_socktype = SOCK_DGRAM;
  71. hints.ai_protocol = IPPROTO_UDP;
  72. hints.ai_flags = AI_ADDRCONFIG;
  73. struct addrinfo *result = nullptr;
  74. if (getaddrinfo(server.hostname.c_str(), server.service.c_str(), &hints, &result) != 0)
  75. continue;
  76. for (auto p = result; p; p = p->ai_next) {
  77. if (p->ai_family == AF_INET) {
  78. char nodebuffer[MAX_NUMERICNODE_LEN];
  79. char servbuffer[MAX_NUMERICSERV_LEN];
  80. if (getnameinfo(p->ai_addr, p->ai_addrlen, nodebuffer, MAX_NUMERICNODE_LEN,
  81. servbuffer, MAX_NUMERICNODE_LEN,
  82. NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
  83. g_object_set(G_OBJECT(mNiceAgent.get()), "stun-server", nodebuffer, nullptr);
  84. g_object_set(G_OBJECT(mNiceAgent.get()), "stun-server-port",
  85. std::stoul(servbuffer), nullptr);
  86. success = true;
  87. break;
  88. }
  89. }
  90. }
  91. freeaddrinfo(result);
  92. if (success)
  93. break;
  94. }
  95. g_signal_connect(G_OBJECT(mNiceAgent.get()), "component-state-changed",
  96. G_CALLBACK(StateChangeCallback), this);
  97. g_signal_connect(G_OBJECT(mNiceAgent.get()), "new-candidate-full",
  98. G_CALLBACK(CandidateCallback), this);
  99. g_signal_connect(G_OBJECT(mNiceAgent.get()), "candidate-gathering-done",
  100. G_CALLBACK(GatheringDoneCallback), this);
  101. mStreamId = nice_agent_add_stream(mNiceAgent.get(), 1);
  102. if (!mStreamId)
  103. throw std::runtime_error("Failed to add a stream");
  104. nice_agent_set_stream_name(mNiceAgent.get(), mStreamId, "application");
  105. nice_agent_set_port_range(mNiceAgent.get(), mStreamId, 1, config.portRangeBegin,
  106. config.portRangeEnd);
  107. nice_agent_attach_recv(mNiceAgent.get(), mStreamId, 1, g_main_loop_get_context(mMainLoop.get()),
  108. RecvCallback, this);
  109. }
  110. IceTransport::~IceTransport() {
  111. g_main_loop_quit(mMainLoop.get());
  112. if (mMainLoopThread.joinable())
  113. mMainLoopThread.join();
  114. }
  115. Description::Role IceTransport::role() const { return mRole; }
  116. IceTransport::State IceTransport::state() const { return mState; }
  117. Description IceTransport::getLocalDescription(Description::Type type) const {
  118. // RFC 5245: The agent that generated the offer which started the ICE processing MUST take the
  119. // controlling role, and the other MUST take the controlled role.
  120. g_object_set(G_OBJECT(mNiceAgent.get()), "controlling-mode",
  121. type == Description::Type::Offer ? TRUE : FALSE, nullptr);
  122. std::unique_ptr<gchar[], void (*)(void *)> sdp(nice_agent_generate_local_sdp(mNiceAgent.get()),
  123. g_free);
  124. return Description(string(sdp.get()), type, mRole);
  125. }
  126. void IceTransport::setRemoteDescription(const Description &description) {
  127. mRole = description.role() == Description::Role::Active ? Description::Role::Passive
  128. : Description::Role::Active;
  129. mMid = description.mid();
  130. if (nice_agent_parse_remote_sdp(mNiceAgent.get(), string(description).c_str()) < 0)
  131. throw std::runtime_error("Failed to parse remote SDP");
  132. }
  133. bool IceTransport::addRemoteCandidate(const Candidate &candidate) {
  134. // Don't try to pass unresolved candidates to libnice for more safety
  135. if (!candidate.isResolved())
  136. return false;
  137. // Warning: the candidate string must start with "a=candidate:" and it must not end with a
  138. // newline, else libnice will reject it.
  139. string sdp(candidate);
  140. NiceCandidate *cand =
  141. nice_agent_parse_remote_candidate_sdp(mNiceAgent.get(), mStreamId, sdp.c_str());
  142. if (!cand)
  143. return false;
  144. GSList *list = g_slist_append(nullptr, cand);
  145. int ret = nice_agent_set_remote_candidates(mNiceAgent.get(), mStreamId, 1, list);
  146. g_slist_free_full(list, reinterpret_cast<GDestroyNotify>(nice_candidate_free));
  147. return ret > 0;
  148. }
  149. void IceTransport::gatherLocalCandidates() {
  150. // Change state now as candidates calls can be synchronous
  151. changeGatheringState(GatheringState::InProgress);
  152. if (!nice_agent_gather_candidates(mNiceAgent.get(), mStreamId)) {
  153. throw std::runtime_error("Failed to gather local ICE candidates");
  154. }
  155. }
  156. bool IceTransport::send(message_ptr message) {
  157. if (!message || !mStreamId)
  158. return false;
  159. outgoing(message);
  160. return true;
  161. }
  162. void IceTransport::incoming(message_ptr message) { recv(message); }
  163. void IceTransport::incoming(const byte *data, int size) {
  164. incoming(make_message(data, data + size));
  165. }
  166. void IceTransport::outgoing(message_ptr message) {
  167. nice_agent_send(mNiceAgent.get(), mStreamId, 1, message->size(),
  168. reinterpret_cast<const char *>(message->data()));
  169. }
  170. void IceTransport::changeState(State state) {
  171. mState = state;
  172. mStateChangeCallback(mState);
  173. }
  174. void IceTransport::changeGatheringState(GatheringState state) {
  175. mGatheringState = state;
  176. mGatheringStateChangeCallback(mGatheringState);
  177. }
  178. void IceTransport::processCandidate(const string &candidate) {
  179. mCandidateCallback(Candidate(candidate, mMid));
  180. }
  181. void IceTransport::processGatheringDone() { changeGatheringState(GatheringState::Complete); }
  182. void IceTransport::processStateChange(uint32_t state) {
  183. if (state != NICE_COMPONENT_STATE_GATHERING)
  184. changeState(static_cast<State>(state));
  185. }
  186. void IceTransport::CandidateCallback(NiceAgent *agent, NiceCandidate *candidate,
  187. gpointer userData) {
  188. auto iceTransport = static_cast<rtc::IceTransport *>(userData);
  189. gchar *cand = nice_agent_generate_local_candidate_sdp(agent, candidate);
  190. try {
  191. iceTransport->processCandidate(cand);
  192. } catch (const std::exception &e) {
  193. std::cerr << "ICE candidate: " << e.what() << std::endl;
  194. }
  195. g_free(cand);
  196. }
  197. void IceTransport::GatheringDoneCallback(NiceAgent *agent, guint streamId, gpointer userData) {
  198. auto iceTransport = static_cast<rtc::IceTransport *>(userData);
  199. try {
  200. iceTransport->processGatheringDone();
  201. } catch (const std::exception &e) {
  202. std::cerr << "ICE gathering done: " << e.what() << std::endl;
  203. }
  204. }
  205. void IceTransport::StateChangeCallback(NiceAgent *agent, guint streamId, guint componentId,
  206. guint state, gpointer userData) {
  207. auto iceTransport = static_cast<rtc::IceTransport *>(userData);
  208. try {
  209. iceTransport->processStateChange(state);
  210. } catch (const std::exception &e) {
  211. std::cerr << "ICE change state: " << e.what() << std::endl;
  212. }
  213. }
  214. void IceTransport::RecvCallback(NiceAgent *agent, guint streamId, guint componentId, guint len,
  215. gchar *buf, gpointer userData) {
  216. auto iceTransport = static_cast<rtc::IceTransport *>(userData);
  217. try {
  218. iceTransport->incoming(reinterpret_cast<byte *>(buf), len);
  219. } catch (const std::exception &e) {
  220. std::cerr << "ICE incoming: " << e.what() << std::endl;
  221. }
  222. }
  223. void IceTransport::LogCallback(const gchar *logDomain, GLogLevelFlags logLevel,
  224. const gchar *message, gpointer userData) {
  225. std::cout << message << std::endl;
  226. }
  227. } // namespace rtc