1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <webrtc/MyWebSocketHandler.h> 18 19 #include "Utils.h" 20 21 #include <json/json.h> 22 23 #include <netdb.h> 24 #include <openssl/rand.h> 25 26 #include <webrtc/Keyboard.h> 27 28 namespace { 29 30 // helper method to ensure a json object has the required fields convertible 31 // to the appropriate types. validateJsonObject(const Json::Value & obj,const std::string & type,const std::vector<std::pair<std::string,Json::ValueType>> & fields,std::function<void (const std::string &)> onError)32 bool validateJsonObject( 33 const Json::Value &obj, const std::string &type, 34 const std::vector<std::pair<std::string, Json::ValueType>> &fields, 35 std::function<void(const std::string&)> onError) { 36 for (const auto &field_spec : fields) { 37 const auto &field_name = field_spec.first; 38 auto field_type = field_spec.second; 39 if (!(obj.isMember(field_name) && 40 obj[field_name].isConvertibleTo(field_type))) { 41 std::string error_msg = "Expected a field named '"; 42 error_msg += field_name + "' of type '"; 43 error_msg += std::to_string(field_type); 44 error_msg += "'"; 45 if (!type.empty()) { 46 error_msg += " in message of type '" + type + "'"; 47 } 48 error_msg += "."; 49 LOG(WARNING) << error_msg; 50 onError(error_msg); 51 return false; 52 } 53 } 54 return true; 55 } 56 57 } // namespace 58 MyWebSocketHandler(std::shared_ptr<RunLoop> runLoop,std::shared_ptr<ServerState> serverState,size_t handlerId)59 MyWebSocketHandler::MyWebSocketHandler( 60 std::shared_ptr<RunLoop> runLoop, 61 std::shared_ptr<ServerState> serverState, 62 size_t handlerId) 63 : mRunLoop(runLoop), 64 mServerState(serverState), 65 mId(handlerId), 66 mOptions(OptionBits::useSingleCertificateForAllTracks), 67 mTouchSink(mServerState->getTouchSink()), 68 mKeyboardSink(mServerState->getKeyboardSink()) { 69 } 70 ~MyWebSocketHandler()71 MyWebSocketHandler::~MyWebSocketHandler() { 72 mServerState->releaseHandlerId(mId); 73 } 74 handleMessage(uint8_t,const uint8_t * msg,size_t len)75 int MyWebSocketHandler::handleMessage( 76 uint8_t /* headerByte */, const uint8_t *msg, size_t len) { 77 // android::hexdump(msg, len); 78 79 Json::Value obj; 80 Json::Reader json_reader; 81 Json::FastWriter json_writer; 82 auto str = reinterpret_cast<const char *>(msg); 83 if (!json_reader.parse(str, str + len, obj) < 0) { 84 return -EINVAL; 85 } 86 87 LOG(VERBOSE) << obj.toStyledString(); 88 89 auto sendMessageOnError = 90 [this](const std::string &error_msg) { 91 auto reply = "{\"error\": \"" + error_msg + "\"}"; 92 sendMessage(reply.c_str(), reply.size()); 93 }; 94 95 if (!validateJsonObject(obj, "", {{"type", Json::ValueType::stringValue}}, 96 sendMessageOnError)) { 97 return -EINVAL; 98 } 99 std::string type = obj["type"].asString(); 100 101 if (type == "greeting") { 102 Json::Value reply; 103 reply["type"] = "hello"; 104 reply["reply"] = "Right back at ya!"; 105 106 auto replyAsString = json_writer.write(reply); 107 sendMessage(replyAsString.c_str(), replyAsString.size()); 108 109 if (obj.isMember("path")) { 110 parseOptions(obj["path"].asString()); 111 } 112 113 if (mOptions & OptionBits::useSingleCertificateForAllTracks) { 114 mCertificateAndKey = CreateDTLSCertificateAndKey(); 115 } 116 117 prepareSessions(); 118 } else if (type == "set-remote-desc") { 119 if (!validateJsonObject(obj, type, 120 {{"sdp", Json::ValueType::stringValue}}, 121 sendMessageOnError)) { 122 return -EINVAL; 123 } 124 125 int err = mOfferedSDP.setTo(obj["sdp"].asString()); 126 127 if (err) { 128 LOG(ERROR) << "Offered SDP could not be parsed (" << err << ")"; 129 } 130 131 for (size_t i = 0; i < mSessions.size(); ++i) { 132 const auto &session = mSessions[i]; 133 134 session->setRemoteParams( 135 getRemoteUFrag(i), 136 getRemotePassword(i), 137 getRemoteFingerprint(i)); 138 } 139 140 return err; 141 } else if (type == "request-offer") { 142 std::stringstream ss; 143 144 ss << 145 "v=0\r\n" 146 "o=- 7794515898627856655 2 IN IP4 127.0.0.1\r\n" 147 "s=-\r\n" 148 "t=0 0\r\n" 149 "a=msid-semantic: WMS pqWEULZNyLiJHA7lcwlUnbule9FJNk0pY0aw\r\n"; 150 151 bool bundled = false; 152 153 if ((mOptions & OptionBits::bundleTracks) && countTracks() > 1) { 154 bundled = true; 155 156 ss << "a=group:BUNDLE 0"; 157 158 if (!(mOptions & OptionBits::disableAudio)) { 159 ss << " 1"; 160 } 161 162 if (mOptions & OptionBits::enableData) { 163 ss << " 2"; 164 } 165 166 ss << "\r\n"; 167 168 emitTrackIceOptionsAndFingerprint(ss, 0 /* mlineIndex */); 169 } 170 171 size_t mlineIndex = 0; 172 173 // Video track (mid = 0) 174 175 std::string videoEncodingSpecific = "a=rtpmap:96 VP8/90000\r\n"; 176 177 videoEncodingSpecific += 178 "a=rtcp-fb:96 ccm fir\r\n" 179 "a=rtcp-fb:96 nack\r\n" 180 "a=rtcp-fb:96 nack pli\r\n"; 181 182 ss << 183 "m=video 9 " 184 << ((mOptions & OptionBits::useTCP) ? "TCP" : "UDP") 185 << "/TLS/RTP/SAVPF 96 97\r\n" 186 "c=IN IP4 0.0.0.0\r\n" 187 "a=rtcp:9 IN IP4 0.0.0.0\r\n"; 188 189 if (!bundled) { 190 emitTrackIceOptionsAndFingerprint(ss, mlineIndex++); 191 } 192 193 ss << 194 "a=setup:actpass\r\n" 195 "a=mid:0\r\n" 196 "a=sendonly\r\n" 197 "a=rtcp-mux\r\n" 198 "a=rtcp-rsize\r\n" 199 "a=rtcp-xr:rcvr-rtt=all\r\n"; 200 201 ss << videoEncodingSpecific << 202 "a=rtpmap:97 rtx/90000\r\n" 203 "a=fmtp:97 apt=96\r\n" 204 "a=ssrc-group:FID 3735928559 3405689008\r\n" 205 "a=ssrc:3735928559 cname:myWebRTP\r\n" 206 "a=ssrc:3735928559 msid:pqWEULZNyLiJHA7lcwlUnbule9FJNk0pY0aw 61843855-edd7-4ca9-be79-4e3ccc6cc035\r\n" 207 "a=ssrc:3735928559 mslabel:pqWEULZNyLiJHA7lcwlUnbule9FJNk0pY0aw\r\n" 208 "a=ssrc:3735928559 label:61843855-edd7-4ca9-be79-4e3ccc6cc035\r\n" 209 "a=ssrc:3405689008 cname:myWebRTP\r\n" 210 "a=ssrc:3405689008 msid:pqWEULZNyLiJHA7lcwlUnbule9FJNk0pY0aw 61843855-edd7-4ca9-be79-4e3ccc6cc035\r\n" 211 "a=ssrc:3405689008 mslabel:pqWEULZNyLiJHA7lcwlUnbule9FJNk0pY0aw\r\n" 212 "a=ssrc:3405689008 label:61843855-edd7-4ca9-be79-4e3ccc6cc035\r\n"; 213 214 if (!(mOptions & OptionBits::disableAudio)) { 215 ss << 216 "m=audio 9 " 217 << ((mOptions & OptionBits::useTCP) ? "TCP" : "UDP") 218 << "/TLS/RTP/SAVPF 98\r\n" 219 "c=IN IP4 0.0.0.0\r\n" 220 "a=rtcp:9 IN IP4 0.0.0.0\r\n"; 221 222 if (!bundled) { 223 emitTrackIceOptionsAndFingerprint(ss, mlineIndex++); 224 } 225 226 ss << 227 "a=setup:actpass\r\n" 228 "a=mid:1\r\n" 229 "a=sendonly\r\n" 230 "a=msid:pqWEULZNyLiJHA7lcwlUnbule9FJNk0pY0aw 61843856-edd7-4ca9-be79-4e3ccc6cc035\r\n" 231 "a=rtcp-mux\r\n" 232 "a=rtcp-rsize\r\n" 233 "a=rtpmap:98 opus/48000/2\r\n" 234 "a=fmtp:98 minptime=10;useinbandfec=1\r\n" 235 "a=ssrc-group:FID 2343432205\r\n" 236 "a=ssrc:2343432205 cname:myWebRTP\r\n" 237 "a=ssrc:2343432205 msid:pqWEULZNyLiJHA7lcwlUnbule9FJNk0pY0aw 61843856-edd7-4ca9-be79-4e3ccc6cc035\r\n" 238 "a=ssrc:2343432205 mslabel:pqWEULZNyLiJHA7lcwlUnbule9FJNk0pY0aw\r\n" 239 "a=ssrc:2343432205 label:61843856-edd7-4ca9-be79-4e3ccc6cc035\r\n"; 240 } 241 242 if (mOptions & OptionBits::enableData) { 243 ss << 244 "m=application 9 " 245 << ((mOptions & OptionBits::useTCP) ? "TCP" : "UDP") 246 << "/DTLS/SCTP webrtc-datachannel\r\n" 247 "c=IN IP4 0.0.0.0\r\n" 248 "a=sctp-port:5000\r\n"; 249 250 if (!bundled) { 251 emitTrackIceOptionsAndFingerprint(ss, mlineIndex++); 252 } 253 254 ss << 255 "a=setup:actpass\r\n" 256 "a=mid:2\r\n" 257 "a=sendrecv\r\n" 258 "a=fmtp:webrtc-datachannel max-message-size=65536\r\n"; 259 } 260 261 Json::Value reply; 262 reply["type"] = "offer"; 263 reply["sdp"] = ss.str(); 264 265 auto replyAsString = json_writer.write(reply); 266 sendMessage(replyAsString.c_str(), replyAsString.size()); 267 } else if (type == "get-ice-candidate") { 268 if (!validateJsonObject(obj, type, {{"mid", Json::ValueType::intValue}}, 269 sendMessageOnError)) { 270 return -EINVAL; 271 } 272 int32_t mid = obj["mid"].asInt(); 273 274 bool success = getCandidate(mid); 275 276 if (!success) { 277 Json::Value reply; 278 reply["type"] = "ice-candidate"; 279 280 auto replyAsString = json_writer.write(reply); 281 sendMessage(replyAsString.c_str(), replyAsString.size()); 282 } 283 } else if (type == "set-mouse-position") { 284 if (!validateJsonObject(obj, type, {{"down", Json::ValueType::intValue}, 285 {"x", Json::ValueType::intValue}, 286 {"y", Json::ValueType::intValue}}, 287 sendMessageOnError)) { 288 return -EINVAL; 289 } 290 int32_t down = obj["down"].asInt(); 291 int32_t x = obj["x"].asInt(); 292 int32_t y = obj["y"].asInt(); 293 294 LOG(VERBOSE) 295 << "set-mouse-position(" << down << ", " << x << ", " << y << ")"; 296 297 mTouchSink->injectTouchEvent(x, y, down != 0); 298 } else if (type == "inject-multi-touch") { 299 if (!validateJsonObject(obj, type, {{"id", Json::ValueType::intValue}, 300 {"initialDown", Json::ValueType::intValue}, 301 {"x", Json::ValueType::intValue}, 302 {"y", Json::ValueType::intValue}, 303 {"slot", Json::ValueType::intValue}}, 304 sendMessageOnError)) { 305 return -EINVAL; 306 } 307 int32_t id = obj["id"].asInt(); 308 int32_t initialDown = obj["initialDown"].asInt(); 309 int32_t x = obj["x"].asInt(); 310 int32_t y = obj["y"].asInt(); 311 int32_t slot = obj["slot"].asInt(); 312 313 LOG(VERBOSE) 314 << "inject-multi-touch id=" 315 << id 316 << ", initialDown=" 317 << initialDown 318 << ", x=" 319 << x 320 << ", y=" 321 << y 322 << ", slot=" 323 << slot; 324 325 mTouchSink->injectMultiTouchEvent(id, slot, x, y, initialDown); 326 } else if (type == "key-event") { 327 if (!validateJsonObject(obj, type, {{"event_type", Json::ValueType::stringValue}, 328 {"keycode", Json::ValueType::stringValue}}, 329 sendMessageOnError)) { 330 return -EINVAL; 331 } 332 auto down = obj["event_type"].asString() == std::string("keydown"); 333 auto code = DomKeyCodeToLinux(obj["keycode"].asString()); 334 mKeyboardSink->injectEvent(down, code); 335 } 336 337 return 0; 338 } 339 countTracks() const340 size_t MyWebSocketHandler::countTracks() const { 341 size_t n = 1; // We always have a video track. 342 343 if (!(mOptions & OptionBits::disableAudio)) { 344 ++n; 345 } 346 347 if (mOptions & OptionBits::enableData) { 348 ++n; 349 } 350 351 return n; 352 } 353 mlineIndexForMid(int32_t mid) const354 ssize_t MyWebSocketHandler::mlineIndexForMid(int32_t mid) const { 355 switch (mid) { 356 case 0: 357 return 0; 358 359 case 1: 360 if (mOptions & OptionBits::disableAudio) { 361 return -1; 362 } 363 364 return 1; 365 366 case 2: 367 if (!(mOptions & OptionBits::enableData)) { 368 return -1; 369 } 370 371 if (mOptions & OptionBits::disableAudio) { 372 return 1; 373 } 374 375 return 2; 376 377 default: 378 return -1; 379 } 380 } 381 getCandidate(int32_t mid)382 bool MyWebSocketHandler::getCandidate(int32_t mid) { 383 auto mlineIndex = mlineIndexForMid(mid); 384 385 if (mlineIndex < 0) { 386 return false; 387 } 388 389 if (!(mOptions & OptionBits::bundleTracks) || mRTPs.empty()) { 390 // Only allocate a local port once if we bundle tracks. 391 392 size_t sessionIndex = mlineIndex; 393 394 uint32_t trackMask = 0; 395 if (mOptions & OptionBits::bundleTracks) { 396 sessionIndex = 0; // One session for all tracks. 397 398 trackMask = RTPSocketHandler::TRACK_VIDEO; 399 400 if (!(mOptions & OptionBits::disableAudio)) { 401 trackMask |= RTPSocketHandler::TRACK_AUDIO; 402 } 403 404 if (mOptions & OptionBits::enableData) { 405 trackMask |= RTPSocketHandler::TRACK_DATA; 406 } 407 } else if (mid == 0) { 408 trackMask = RTPSocketHandler::TRACK_VIDEO; 409 } else if (mid == 1) { 410 trackMask = RTPSocketHandler::TRACK_AUDIO; 411 } else { 412 trackMask = RTPSocketHandler::TRACK_DATA; 413 } 414 415 const auto &session = mSessions[sessionIndex]; 416 417 auto rtp = std::make_shared<RTPSocketHandler>( 418 mRunLoop, 419 mServerState, 420 (mOptions & OptionBits::useTCP) 421 ? RTPSocketHandler::TransportType::TCP 422 : RTPSocketHandler::TransportType::UDP, 423 PF_INET, 424 trackMask, 425 session); 426 427 rtp->run(); 428 429 mRTPs.push_back(rtp); 430 } 431 432 auto rtp = mRTPs.back(); 433 434 Json::Value reply; 435 reply["type"] = "ice-candidate"; 436 437 auto localIPString = rtp->getLocalIPString(); 438 439 std::stringstream ss; 440 ss << "candidate:0 1 "; 441 442 if (mOptions & OptionBits::useTCP) { 443 ss << "tcp"; 444 } else { 445 ss << "UDP"; 446 } 447 448 // see rfc8445, 5.1.2.1. for the derivation of "2122121471" below. 449 ss << " 2122121471 " << localIPString << " " << rtp->getLocalPort() << " typ host "; 450 451 if (mOptions & OptionBits::useTCP) { 452 ss << "tcptype passive "; 453 } 454 455 ss << "generation 0 ufrag " << rtp->getLocalUFrag(); 456 457 reply["candidate"] = ss.str(); 458 reply["mlineIndex"] = static_cast<Json::UInt64>(mlineIndex); 459 460 Json::FastWriter json_writer; 461 auto replyAsString = json_writer.write(reply); 462 sendMessage(replyAsString.c_str(), replyAsString.size()); 463 464 return true; 465 } 466 getSDPValue(ssize_t targetMediaIndex,std::string_view key,bool fallthroughToGeneralSection) const467 std::optional<std::string> MyWebSocketHandler::getSDPValue( 468 ssize_t targetMediaIndex, 469 std::string_view key, 470 bool fallthroughToGeneralSection) const { 471 472 CHECK_GE(targetMediaIndex, -1); 473 474 if (targetMediaIndex + 1 >= mOfferedSDP.countSections()) { 475 LOG(ERROR) 476 << "getSDPValue: targetMediaIndex " 477 << targetMediaIndex 478 << " out of range (countSections()=" 479 << mOfferedSDP.countSections() 480 << ")"; 481 482 return std::nullopt; 483 } 484 485 const std::string prefix = "a=" + std::string(key) + ":"; 486 487 auto sectionIndex = 1 + targetMediaIndex; 488 auto rangeEnd = mOfferedSDP.section_end(sectionIndex); 489 490 auto it = std::find_if( 491 mOfferedSDP.section_begin(sectionIndex), 492 rangeEnd, 493 [prefix](const auto &line) { 494 return StartsWith(line, prefix); 495 }); 496 497 if (it == rangeEnd) { 498 if (fallthroughToGeneralSection) { 499 CHECK_NE(targetMediaIndex, -1); 500 501 // Oh no, scary recursion ahead. 502 return getSDPValue( 503 -1 /* targetMediaIndex */, 504 key, 505 false /* fallthroughToGeneralSection */); 506 } 507 508 LOG(WARNING) 509 << "Unable to find '" 510 << prefix 511 << "' with targetMediaIndex=" 512 << targetMediaIndex; 513 514 return std::nullopt; 515 } 516 517 return (*it).substr(prefix.size()); 518 } 519 getRemotePassword(size_t mlineIndex) const520 std::string MyWebSocketHandler::getRemotePassword(size_t mlineIndex) const { 521 auto value = getSDPValue( 522 mlineIndex, "ice-pwd", true /* fallthroughToGeneralSection */); 523 524 return value ? *value : std::string(); 525 } 526 getRemoteUFrag(size_t mlineIndex) const527 std::string MyWebSocketHandler::getRemoteUFrag(size_t mlineIndex) const { 528 auto value = getSDPValue( 529 mlineIndex, "ice-ufrag", true /* fallthroughToGeneralSection */); 530 531 return value ? *value : std::string(); 532 } 533 getRemoteFingerprint(size_t mlineIndex) const534 std::string MyWebSocketHandler::getRemoteFingerprint(size_t mlineIndex) const { 535 auto value = getSDPValue( 536 mlineIndex, "fingerprint", true /* fallthroughToGeneralSection */); 537 538 return value ? *value : std::string(); 539 } 540 541 // static 542 std::pair<std::shared_ptr<X509>, std::shared_ptr<EVP_PKEY>> CreateDTLSCertificateAndKey()543 MyWebSocketHandler::CreateDTLSCertificateAndKey() { 544 // Modeled after "https://stackoverflow.com/questions/256405/ 545 // programmatically-create-x509-certificate-using-openssl". 546 547 std::shared_ptr<EVP_PKEY> pkey(EVP_PKEY_new(), EVP_PKEY_free); 548 549 std::unique_ptr<RSA, std::function<void(RSA *)>> rsa( 550 RSA_new(), RSA_free); 551 552 BIGNUM exponent; 553 BN_init(&exponent); 554 BN_set_word(&exponent, RSA_F4); 555 556 int res = RSA_generate_key_ex( 557 rsa.get() /* rsa */, 2048, &exponent, nullptr /* callback */); 558 559 CHECK_EQ(res, 1); 560 561 EVP_PKEY_assign_RSA(pkey.get(), rsa.release()); 562 563 std::shared_ptr<X509> x509(X509_new(), X509_free); 564 565 ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), 1); 566 567 X509_gmtime_adj(X509_get_notBefore(x509.get()), 0); 568 X509_gmtime_adj(X509_get_notAfter(x509.get()), 60 * 60 * 24 * 7); // 7 days. 569 570 X509_set_pubkey(x509.get(), pkey.get()); 571 572 X509_NAME *name = X509_get_subject_name(x509.get()); 573 574 X509_NAME_add_entry_by_txt( 575 name, "C", MBSTRING_ASC, (unsigned char *)"US", -1, -1, 0); 576 577 X509_NAME_add_entry_by_txt( 578 name, 579 "O", 580 MBSTRING_ASC, 581 (unsigned char *)"Beyond Aggravated", 582 -1, 583 -1, 584 0); 585 586 X509_NAME_add_entry_by_txt( 587 name, "CN", MBSTRING_ASC, (unsigned char *)"localhost", -1, -1, 0); 588 589 X509_set_issuer_name(x509.get(), name); 590 591 auto digest = EVP_sha256(); 592 593 X509_sign(x509.get(), pkey.get(), digest); 594 595 return std::make_pair(x509, pkey); 596 } 597 parseOptions(const std::string & pathAndQuery)598 void MyWebSocketHandler::parseOptions(const std::string &pathAndQuery) { 599 auto separatorPos = pathAndQuery.find('?'); 600 601 if (separatorPos == std::string::npos) { 602 return; 603 } 604 605 auto components = SplitString(pathAndQuery.substr(separatorPos + 1), '&'); 606 for (auto name : components) { 607 bool boolValue = true; 608 609 separatorPos = name.find('='); 610 if (separatorPos != std::string::npos) { 611 boolValue = false; 612 613 auto value = name.substr(separatorPos + 1); 614 name.erase(separatorPos); 615 616 boolValue = 617 !strcasecmp("true", value.c_str()) 618 || !strcasecmp("yes", value.c_str()) 619 || value == "1"; 620 } 621 622 if (name == "disable_audio") { 623 auto mask = OptionBits::disableAudio; 624 mOptions = (mOptions & ~mask) | (boolValue ? mask : 0); 625 } else if (name == "bundle_tracks" && boolValue) { 626 auto mask = OptionBits::bundleTracks; 627 mOptions = (mOptions & ~mask) | (boolValue ? mask : 0); 628 } else if (name == "enable_data" && boolValue) { 629 auto mask = OptionBits::enableData; 630 mOptions = (mOptions & ~mask) | (boolValue ? mask : 0); 631 } else if (name == "use_tcp" && boolValue) { 632 auto mask = OptionBits::useTCP; 633 mOptions = (mOptions & ~mask) | (boolValue ? mask : 0); 634 } 635 } 636 } 637 638 // static CreateRandomIceCharSequence(char * dst,size_t size)639 void MyWebSocketHandler::CreateRandomIceCharSequence(char *dst, size_t size) { 640 // Per RFC 5245 an ice-char is alphanumeric, '+' or '/', i.e. 64 distinct 641 // character values (6 bit). 642 643 CHECK_EQ(1, RAND_bytes(reinterpret_cast<unsigned char *>(dst), size)); 644 645 for (size_t i = 0; i < size; ++i) { 646 char x = dst[i] & 0x3f; 647 if (x < 26) { 648 x += 'a'; 649 } else if (x < 52) { 650 x += 'A' - 26; 651 } else if (x < 62) { 652 x += '0' - 52; 653 } else if (x < 63) { 654 x = '+'; 655 } else { 656 x = '/'; 657 } 658 659 dst[i] = x; 660 } 661 } 662 663 std::pair<std::string, std::string> createUniqueUFragAndPassword()664 MyWebSocketHandler::createUniqueUFragAndPassword() { 665 // RFC 5245, section 15.4 mandates that uFrag is at least 4 and password 666 // at least 22 ice-chars long. 667 668 char uFragChars[4]; 669 670 for (;;) { 671 CreateRandomIceCharSequence(uFragChars, sizeof(uFragChars)); 672 673 std::string uFrag(uFragChars, sizeof(uFragChars)); 674 675 auto it = std::find_if( 676 mSessions.begin(), mSessions.end(), 677 [uFrag](const auto &session) { 678 return session->localUFrag() == uFrag; 679 }); 680 681 if (it == mSessions.end()) { 682 // This uFrag is not in use yet. 683 break; 684 } 685 } 686 687 char passwordChars[22]; 688 CreateRandomIceCharSequence(passwordChars, sizeof(passwordChars)); 689 690 return std::make_pair( 691 std::string(uFragChars, sizeof(uFragChars)), 692 std::string(passwordChars, sizeof(passwordChars))); 693 } 694 prepareSessions()695 void MyWebSocketHandler::prepareSessions() { 696 size_t numSessions = 697 (mOptions & OptionBits::bundleTracks) ? 1 : countTracks(); 698 699 for (size_t i = 0; i < numSessions; ++i) { 700 auto [ufrag, password] = createUniqueUFragAndPassword(); 701 702 auto [certificate, key] = 703 (mOptions & OptionBits::useSingleCertificateForAllTracks) 704 ? mCertificateAndKey : CreateDTLSCertificateAndKey(); 705 706 mSessions.push_back( 707 std::make_shared<RTPSession>( 708 ufrag, password, certificate, key)); 709 } 710 } 711 emitTrackIceOptionsAndFingerprint(std::stringstream & ss,size_t mlineIndex) const712 void MyWebSocketHandler::emitTrackIceOptionsAndFingerprint( 713 std::stringstream &ss, size_t mlineIndex) const { 714 CHECK_LT(mlineIndex, mSessions.size()); 715 const auto &session = mSessions[mlineIndex]; 716 717 ss << "a=ice-ufrag:" << session->localUFrag() << "\r\n"; 718 ss << "a=ice-pwd:" << session->localPassword() << "\r\n"; 719 ss << "a=ice-options:trickle\r\n"; 720 ss << "a=fingerprint:" << session->localFingerprint() << "\r\n"; 721 } 722