• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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