• 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