candidate.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. /**
  2. * Copyright (c) 2019 Paul-Louis Ageneau
  3. *
  4. * This Source Code Form is subject to the terms of the Mozilla Public
  5. * License, v. 2.0. If a copy of the MPL was not distributed with this
  6. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
  7. */
  8. #include "candidate.hpp"
  9. #include "impl/internals.hpp"
  10. #include <algorithm>
  11. #include <array>
  12. #include <cctype>
  13. #include <sstream>
  14. #include <unordered_map>
  15. #ifdef _WIN32
  16. #include <winsock2.h>
  17. #include <ws2tcpip.h>
  18. #else
  19. #include <netdb.h>
  20. #include <netinet/in.h>
  21. #include <sys/socket.h>
  22. #endif
  23. #include <sys/types.h>
  24. using std::array;
  25. using std::string;
  26. namespace {
  27. inline bool match_prefix(const string &str, const string &prefix) {
  28. return str.size() >= prefix.size() &&
  29. std::mismatch(prefix.begin(), prefix.end(), str.begin()).first == prefix.end();
  30. }
  31. inline void trim_begin(string &str) {
  32. str.erase(str.begin(),
  33. std::find_if(str.begin(), str.end(), [](char c) { return !std::isspace(c); }));
  34. }
  35. inline void trim_end(string &str) {
  36. str.erase(
  37. std::find_if(str.rbegin(), str.rend(), [](char c) { return !std::isspace(c); }).base(),
  38. str.end());
  39. }
  40. } // namespace
  41. namespace rtc {
  42. Candidate::Candidate()
  43. : mFoundation("none"), mComponent(0), mPriority(0), mTypeString("unknown"),
  44. mTransportString("unknown"), mType(Type::Unknown), mTransportType(TransportType::Unknown),
  45. mNode("0.0.0.0"), mService("9"), mFamily(Family::Unresolved), mPort(0) {}
  46. Candidate::Candidate(string candidate) : Candidate() {
  47. if (!candidate.empty())
  48. parse(std::move(candidate));
  49. }
  50. Candidate::Candidate(string candidate, string mid) : Candidate() {
  51. if (!candidate.empty())
  52. parse(std::move(candidate));
  53. if (!mid.empty())
  54. mMid.emplace(std::move(mid));
  55. }
  56. void Candidate::parse(string candidate) {
  57. using TypeMap_t = std::unordered_map<string, Type>;
  58. using TcpTypeMap_t = std::unordered_map<string, TransportType>;
  59. static const TypeMap_t TypeMap = {{"host", Type::Host},
  60. {"srflx", Type::ServerReflexive},
  61. {"prflx", Type::PeerReflexive},
  62. {"relay", Type::Relayed}};
  63. static const TcpTypeMap_t TcpTypeMap = {{"active", TransportType::TcpActive},
  64. {"passive", TransportType::TcpPassive},
  65. {"so", TransportType::TcpSo}};
  66. const std::array prefixes{"a=", "candidate:"};
  67. for (string prefix : prefixes)
  68. if (match_prefix(candidate, prefix))
  69. candidate.erase(0, prefix.size());
  70. PLOG_VERBOSE << "Parsing candidate: " << candidate;
  71. // See RFC 8445 for format
  72. std::istringstream iss(candidate);
  73. string typ_;
  74. if (!(iss >> mFoundation >> mComponent >> mTransportString >> mPriority &&
  75. iss >> mNode >> mService >> typ_ >> mTypeString && typ_ == "typ"))
  76. throw std::invalid_argument("Invalid candidate format");
  77. std::getline(iss, mTail);
  78. trim_begin(mTail);
  79. trim_end(mTail);
  80. if (auto it = TypeMap.find(mTypeString); it != TypeMap.end())
  81. mType = it->second;
  82. else
  83. mType = Type::Unknown;
  84. if (mTransportString == "UDP" || mTransportString == "udp") {
  85. mTransportType = TransportType::Udp;
  86. } else if (mTransportString == "TCP" || mTransportString == "tcp") {
  87. // Peek tail to find TCP type
  88. std::istringstream tiss(mTail);
  89. string tcptype_, tcptype;
  90. if (tiss >> tcptype_ >> tcptype && tcptype_ == "tcptype") {
  91. if (auto it = TcpTypeMap.find(tcptype); it != TcpTypeMap.end())
  92. mTransportType = it->second;
  93. else
  94. mTransportType = TransportType::TcpUnknown;
  95. } else {
  96. mTransportType = TransportType::TcpUnknown;
  97. }
  98. } else {
  99. mTransportType = TransportType::Unknown;
  100. }
  101. }
  102. void Candidate::hintMid(string mid) {
  103. if (!mMid)
  104. mMid.emplace(std::move(mid));
  105. }
  106. void Candidate::changeAddress(string addr) { changeAddress(std::move(addr), mService); }
  107. void Candidate::changeAddress(string addr, uint16_t port) {
  108. changeAddress(std::move(addr), std::to_string(port));
  109. }
  110. void Candidate::changeAddress(string addr, string service) {
  111. mNode = std::move(addr);
  112. mService = std::move(service);
  113. mFamily = Family::Unresolved;
  114. mAddress.clear();
  115. mPort = 0;
  116. if (!resolve(ResolveMode::Simple))
  117. throw std::invalid_argument("Invalid candidate address \"" + addr + ":" + service + "\"");
  118. }
  119. bool Candidate::resolve(ResolveMode mode) {
  120. PLOG_VERBOSE << "Resolving candidate (mode="
  121. << (mode == ResolveMode::Simple ? "simple" : "lookup") << "): " << mNode << ' '
  122. << mService;
  123. // Try to resolve the node and service
  124. struct addrinfo hints = {};
  125. hints.ai_family = AF_UNSPEC;
  126. hints.ai_flags = AI_ADDRCONFIG;
  127. if (mTransportType == TransportType::Udp) {
  128. hints.ai_socktype = SOCK_DGRAM;
  129. hints.ai_protocol = IPPROTO_UDP;
  130. } else if (mTransportType != TransportType::Unknown) {
  131. hints.ai_socktype = SOCK_STREAM;
  132. hints.ai_protocol = IPPROTO_TCP;
  133. }
  134. if (mode == ResolveMode::Simple)
  135. hints.ai_flags |= AI_NUMERICHOST;
  136. struct addrinfo *result = nullptr;
  137. if (getaddrinfo(mNode.c_str(), mService.c_str(), &hints, &result) == 0) {
  138. for (auto p = result; p; p = p->ai_next) {
  139. if (p->ai_family == AF_INET || p->ai_family == AF_INET6) {
  140. char nodebuffer[MAX_NUMERICNODE_LEN];
  141. char servbuffer[MAX_NUMERICSERV_LEN];
  142. if (getnameinfo(p->ai_addr, socklen_t(p->ai_addrlen), nodebuffer,
  143. MAX_NUMERICNODE_LEN, servbuffer, MAX_NUMERICSERV_LEN,
  144. NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
  145. try {
  146. mPort = uint16_t(std::stoul(servbuffer));
  147. } catch (...) {
  148. return false;
  149. }
  150. mAddress = nodebuffer;
  151. mFamily = p->ai_family == AF_INET6 ? Family::Ipv6 : Family::Ipv4;
  152. PLOG_VERBOSE << "Resolved candidate: " << mAddress << ' ' << mPort;
  153. break;
  154. }
  155. }
  156. }
  157. freeaddrinfo(result);
  158. }
  159. return mFamily != Family::Unresolved;
  160. }
  161. Candidate::Type Candidate::type() const { return mType; }
  162. Candidate::TransportType Candidate::transportType() const { return mTransportType; }
  163. uint32_t Candidate::priority() const { return mPriority; }
  164. string Candidate::candidate() const {
  165. const char sp{' '};
  166. std::ostringstream oss;
  167. oss << "candidate:";
  168. oss << mFoundation << sp << mComponent << sp << mTransportString << sp << mPriority << sp;
  169. if (isResolved())
  170. oss << mAddress << sp << mPort;
  171. else
  172. oss << mNode << sp << mService;
  173. oss << sp << "typ" << sp << mTypeString;
  174. if (!mTail.empty())
  175. oss << sp << mTail;
  176. return oss.str();
  177. }
  178. string Candidate::mid() const { return mMid.value_or("0"); }
  179. Candidate::operator string() const {
  180. std::ostringstream line;
  181. line << "a=" << candidate();
  182. return line.str();
  183. }
  184. bool Candidate::operator==(const Candidate &other) const {
  185. return (mFoundation == other.mFoundation && mService == other.mService && mNode == other.mNode);
  186. }
  187. bool Candidate::operator!=(const Candidate &other) const {
  188. return mFoundation != other.mFoundation;
  189. }
  190. bool Candidate::isResolved() const { return mFamily != Family::Unresolved; }
  191. Candidate::Family Candidate::family() const { return mFamily; }
  192. optional<string> Candidate::address() const {
  193. return isResolved() ? std::make_optional(mAddress) : nullopt;
  194. }
  195. optional<uint16_t> Candidate::port() const {
  196. return isResolved() ? std::make_optional(mPort) : nullopt;
  197. }
  198. std::ostream &operator<<(std::ostream &out, const Candidate &candidate) {
  199. return out << string(candidate);
  200. }
  201. std::ostream &operator<<(std::ostream &out, const Candidate::Type &type) {
  202. switch (type) {
  203. case Candidate::Type::Host:
  204. return out << "host";
  205. case Candidate::Type::PeerReflexive:
  206. return out << "prflx";
  207. case Candidate::Type::ServerReflexive:
  208. return out << "srflx";
  209. case Candidate::Type::Relayed:
  210. return out << "relay";
  211. default:
  212. return out << "unknown";
  213. }
  214. }
  215. std::ostream &operator<<(std::ostream &out, const Candidate::TransportType &transportType) {
  216. switch (transportType) {
  217. case Candidate::TransportType::Udp:
  218. return out << "UDP";
  219. case Candidate::TransportType::TcpActive:
  220. return out << "TCP_active";
  221. case Candidate::TransportType::TcpPassive:
  222. return out << "TCP_passive";
  223. case Candidate::TransportType::TcpSo:
  224. return out << "TCP_so";
  225. case Candidate::TransportType::TcpUnknown:
  226. return out << "TCP_unknown";
  227. default:
  228. return out << "unknown";
  229. }
  230. }
  231. } // namespace rtc