tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SdpAttribute.cpp (38849B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "sdp/SdpAttribute.h"
      8 
      9 #include <bitset>
     10 #include <iomanip>
     11 
     12 #include "sdp/SdpHelper.h"
     13 
     14 #ifdef CRLF
     15 #  undef CRLF
     16 #endif
     17 #define CRLF "\r\n"
     18 
     19 namespace mozilla {
     20 
     21 static unsigned char PeekChar(std::istream& is, std::string* error) {
     22  int next = is.peek();
     23  if (next == EOF) {
     24    *error = "Truncated";
     25    return 0;
     26  }
     27 
     28  return next;
     29 }
     30 
     31 static std::string ParseToken(std::istream& is, const std::string& delims,
     32                              std::string* error) {
     33  std::string token;
     34  while (is) {
     35    unsigned char c = PeekChar(is, error);
     36    if (!c || (delims.find(c) != std::string::npos)) {
     37      break;
     38    }
     39    token.push_back(std::tolower(is.get()));
     40  }
     41  return token;
     42 }
     43 
     44 static bool SkipChar(std::istream& is, unsigned char c, std::string* error) {
     45  if (PeekChar(is, error) != c) {
     46    *error = "Expected \'";
     47    error->push_back(c);
     48    error->push_back('\'');
     49    return false;
     50  }
     51 
     52  is.get();
     53  return true;
     54 }
     55 
     56 void SdpConnectionAttribute::Serialize(std::ostream& os) const {
     57  os << "a=" << mType << ":" << mValue << CRLF;
     58 }
     59 
     60 void SdpDirectionAttribute::Serialize(std::ostream& os) const {
     61  os << "a=" << mValue << CRLF;
     62 }
     63 
     64 void SdpDtlsMessageAttribute::Serialize(std::ostream& os) const {
     65  os << "a=" << mType << ":" << mRole << " " << mValue << CRLF;
     66 }
     67 
     68 bool SdpDtlsMessageAttribute::Parse(std::istream& is, std::string* error) {
     69  std::string roleToken = ParseToken(is, " ", error);
     70  if (roleToken == "server") {
     71    mRole = kServer;
     72  } else if (roleToken == "client") {
     73    mRole = kClient;
     74  } else {
     75    *error = "Invalid dtls-message role; must be either client or server";
     76    return false;
     77  }
     78 
     79  is >> std::ws;
     80 
     81  std::string s(std::istreambuf_iterator<char>(is), {});
     82  mValue = s;
     83 
     84  return true;
     85 }
     86 
     87 void SdpExtmapAttributeList::Serialize(std::ostream& os) const {
     88  for (auto i = mExtmaps.begin(); i != mExtmaps.end(); ++i) {
     89    os << "a=" << mType << ":" << i->entry;
     90    if (i->direction_specified) {
     91      os << "/" << i->direction;
     92    }
     93    os << " " << i->extensionname;
     94    if (i->extensionattributes.length()) {
     95      os << " " << i->extensionattributes;
     96    }
     97    os << CRLF;
     98  }
     99 }
    100 
    101 void SdpFingerprintAttributeList::Serialize(std::ostream& os) const {
    102  for (auto i = mFingerprints.begin(); i != mFingerprints.end(); ++i) {
    103    os << "a=" << mType << ":" << i->hashFunc << " "
    104       << FormatFingerprint(i->fingerprint) << CRLF;
    105  }
    106 }
    107 
    108 // Format the fingerprint in RFC 4572 Section 5 attribute format
    109 std::string SdpFingerprintAttributeList::FormatFingerprint(
    110    const std::vector<uint8_t>& fp) {
    111  if (fp.empty()) {
    112    MOZ_ASSERT(false, "Cannot format an empty fingerprint.");
    113    return "";
    114  }
    115 
    116  std::ostringstream os;
    117  for (auto i = fp.begin(); i != fp.end(); ++i) {
    118    os << ":" << std::hex << std::uppercase << std::setw(2) << std::setfill('0')
    119       << static_cast<uint32_t>(*i);
    120  }
    121  return os.str().substr(1);
    122 }
    123 
    124 static uint8_t FromUppercaseHex(char ch) {
    125  if ((ch >= '0') && (ch <= '9')) {
    126    return ch - '0';
    127  }
    128  if ((ch >= 'A') && (ch <= 'F')) {
    129    return ch - 'A' + 10;
    130  }
    131  return 16;  // invalid
    132 }
    133 
    134 // Parse the fingerprint from RFC 4572 Section 5 attribute format
    135 std::vector<uint8_t> SdpFingerprintAttributeList::ParseFingerprint(
    136    const std::string& str) {
    137  size_t targetSize = (str.length() + 1) / 3;
    138  std::vector<uint8_t> fp(targetSize);
    139  size_t fpIndex = 0;
    140 
    141  if (str.length() % 3 != 2) {
    142    fp.clear();
    143    return fp;
    144  }
    145 
    146  for (size_t i = 0; i < str.length(); i += 3) {
    147    uint8_t high = FromUppercaseHex(str[i]);
    148    uint8_t low = FromUppercaseHex(str[i + 1]);
    149    if (high > 0xf || low > 0xf ||
    150        (i + 2 < str.length() && str[i + 2] != ':')) {
    151      fp.clear();  // error
    152      return fp;
    153    }
    154    fp[fpIndex++] = high << 4 | low;
    155  }
    156  return fp;
    157 }
    158 
    159 bool SdpFmtpAttributeList::operator==(const SdpFmtpAttributeList& other) const {
    160  return mFmtps == other.mFmtps;
    161 }
    162 
    163 void SdpFmtpAttributeList::Serialize(std::ostream& os) const {
    164  for (auto i = mFmtps.begin(); i != mFmtps.end(); ++i) {
    165    if (i->parameters && i->parameters->ShouldSerialize()) {
    166      os << "a=" << mType << ":" << i->format << " ";
    167      i->parameters->Serialize(os);
    168      os << CRLF;
    169    }
    170  }
    171 }
    172 
    173 void SdpGroupAttributeList::Serialize(std::ostream& os) const {
    174  for (auto i = mGroups.begin(); i != mGroups.end(); ++i) {
    175    os << "a=" << mType << ":" << i->semantics;
    176    for (auto j = i->tags.begin(); j != i->tags.end(); ++j) {
    177      os << " " << (*j);
    178    }
    179    os << CRLF;
    180  }
    181 }
    182 
    183 // We're just using an SdpStringAttribute for this right now
    184 #if 0
    185 void SdpIdentityAttribute::Serialize(std::ostream& os) const
    186 {
    187  os << "a=" << mType << ":" << mAssertion;
    188  for (auto i = mExtensions.begin(); i != mExtensions.end(); i++) {
    189    os << (i == mExtensions.begin() ? " " : ";") << (*i);
    190  }
    191  os << CRLF;
    192 }
    193 #endif
    194 
    195 // Class to help with omitting a leading delimiter for the first item in a list
    196 class SkipFirstDelimiter {
    197 public:
    198  explicit SkipFirstDelimiter(const std::string& delim)
    199      : mDelim(delim), mFirst(true) {}
    200 
    201  std::ostream& print(std::ostream& os) {
    202    if (!mFirst) {
    203      os << mDelim;
    204    }
    205    mFirst = false;
    206    return os;
    207  }
    208 
    209 private:
    210  std::string mDelim;
    211  bool mFirst;
    212 };
    213 
    214 static std::ostream& operator<<(std::ostream& os, SkipFirstDelimiter& delim) {
    215  return delim.print(os);
    216 }
    217 
    218 void SdpImageattrAttributeList::XYRange::Serialize(std::ostream& os) const {
    219  if (discreteValues.empty()) {
    220    os << "[" << min << ":";
    221    if (step != 1) {
    222      os << step << ":";
    223    }
    224    os << max << "]";
    225  } else if (discreteValues.size() == 1) {
    226    os << discreteValues.front();
    227  } else {
    228    os << "[";
    229    SkipFirstDelimiter comma(",");
    230    for (auto value : discreteValues) {
    231      os << comma << value;
    232    }
    233    os << "]";
    234  }
    235 }
    236 
    237 template <typename T>
    238 bool GetUnsigned(std::istream& is, T min, T max, T* value, std::string* error) {
    239  if (PeekChar(is, error) == '-') {
    240    *error = "Value is less than 0";
    241    return false;
    242  }
    243 
    244  is >> std::noskipws >> *value;
    245 
    246  if (is.fail()) {
    247    *error = "Malformed";
    248    return false;
    249  }
    250 
    251  if (*value < min) {
    252    *error = "Value too small";
    253    return false;
    254  }
    255 
    256  if (*value > max) {
    257    *error = "Value too large";
    258    return false;
    259  }
    260 
    261  return true;
    262 }
    263 
    264 static bool GetXYValue(std::istream& is, uint32_t* value, std::string* error) {
    265  return GetUnsigned<uint32_t>(is, 1, 999999, value, error);
    266 }
    267 
    268 bool SdpImageattrAttributeList::XYRange::ParseDiscreteValues(
    269    std::istream& is, std::string* error) {
    270  do {
    271    uint32_t value;
    272    if (!GetXYValue(is, &value, error)) {
    273      return false;
    274    }
    275    discreteValues.push_back(value);
    276  } while (SkipChar(is, ',', error));
    277 
    278  return SkipChar(is, ']', error);
    279 }
    280 
    281 bool SdpImageattrAttributeList::XYRange::ParseAfterMin(std::istream& is,
    282                                                       std::string* error) {
    283  // We have already parsed "[320:", and now expect another uint
    284  uint32_t value;
    285  if (!GetXYValue(is, &value, error)) {
    286    return false;
    287  }
    288 
    289  if (SkipChar(is, ':', error)) {
    290    // Range with step eg [320:16:640]
    291    step = value;
    292    // Now |value| should be the max
    293    if (!GetXYValue(is, &value, error)) {
    294      return false;
    295    }
    296  }
    297 
    298  max = value;
    299  if (min >= max) {
    300    *error = "Min is not smaller than max";
    301    return false;
    302  }
    303 
    304  return SkipChar(is, ']', error);
    305 }
    306 
    307 bool SdpImageattrAttributeList::XYRange::ParseAfterBracket(std::istream& is,
    308                                                           std::string* error) {
    309  // Either a range, or a list of discrete values
    310  // [320:640], [320:16:640], or [320,640]
    311  uint32_t value;
    312  if (!GetXYValue(is, &value, error)) {
    313    return false;
    314  }
    315 
    316  if (SkipChar(is, ':', error)) {
    317    // Range - [640:480] or [640:16:480]
    318    min = value;
    319    return ParseAfterMin(is, error);
    320  }
    321 
    322  if (SkipChar(is, ',', error)) {
    323    discreteValues.push_back(value);
    324    return ParseDiscreteValues(is, error);
    325  }
    326 
    327  *error = "Expected \':\' or \',\'";
    328  return false;
    329 }
    330 
    331 bool SdpImageattrAttributeList::XYRange::Parse(std::istream& is,
    332                                               std::string* error) {
    333  if (SkipChar(is, '[', error)) {
    334    return ParseAfterBracket(is, error);
    335  }
    336 
    337  // Single discrete value
    338  uint32_t value;
    339  if (!GetXYValue(is, &value, error)) {
    340    return false;
    341  }
    342  discreteValues.push_back(value);
    343 
    344  return true;
    345 }
    346 
    347 static bool GetSPValue(std::istream& is, float* value, std::string* error) {
    348  return GetUnsigned<float>(is, 0.1f, 9.9999f, value, error);
    349 }
    350 
    351 static bool GetQValue(std::istream& is, float* value, std::string* error) {
    352  return GetUnsigned<float>(is, 0.0f, 1.0f, value, error);
    353 }
    354 
    355 bool SdpImageattrAttributeList::SRange::ParseDiscreteValues(
    356    std::istream& is, std::string* error) {
    357  do {
    358    float value;
    359    if (!GetSPValue(is, &value, error)) {
    360      return false;
    361    }
    362    discreteValues.push_back(value);
    363  } while (SkipChar(is, ',', error));
    364 
    365  return SkipChar(is, ']', error);
    366 }
    367 
    368 bool SdpImageattrAttributeList::SRange::ParseAfterMin(std::istream& is,
    369                                                      std::string* error) {
    370  if (!GetSPValue(is, &max, error)) {
    371    return false;
    372  }
    373 
    374  if (min >= max) {
    375    *error = "Min is not smaller than max";
    376    return false;
    377  }
    378 
    379  return SkipChar(is, ']', error);
    380 }
    381 
    382 bool SdpImageattrAttributeList::SRange::ParseAfterBracket(std::istream& is,
    383                                                          std::string* error) {
    384  // Either a range, or a list of discrete values
    385  float value;
    386  if (!GetSPValue(is, &value, error)) {
    387    return false;
    388  }
    389 
    390  if (SkipChar(is, '-', error)) {
    391    min = value;
    392    return ParseAfterMin(is, error);
    393  }
    394 
    395  if (SkipChar(is, ',', error)) {
    396    discreteValues.push_back(value);
    397    return ParseDiscreteValues(is, error);
    398  }
    399 
    400  *error = "Expected either \'-\' or \',\'";
    401  return false;
    402 }
    403 
    404 bool SdpImageattrAttributeList::SRange::Parse(std::istream& is,
    405                                              std::string* error) {
    406  if (SkipChar(is, '[', error)) {
    407    return ParseAfterBracket(is, error);
    408  }
    409 
    410  // Single discrete value
    411  float value;
    412  if (!GetSPValue(is, &value, error)) {
    413    return false;
    414  }
    415  discreteValues.push_back(value);
    416  return true;
    417 }
    418 
    419 bool SdpImageattrAttributeList::PRange::Parse(std::istream& is,
    420                                              std::string* error) {
    421  if (!SkipChar(is, '[', error)) {
    422    return false;
    423  }
    424 
    425  if (!GetSPValue(is, &min, error)) {
    426    return false;
    427  }
    428 
    429  if (!SkipChar(is, '-', error)) {
    430    return false;
    431  }
    432 
    433  if (!GetSPValue(is, &max, error)) {
    434    return false;
    435  }
    436 
    437  if (min >= max) {
    438    *error = "min must be smaller than max";
    439    return false;
    440  }
    441 
    442  if (!SkipChar(is, ']', error)) {
    443    return false;
    444  }
    445  return true;
    446 }
    447 
    448 void SdpImageattrAttributeList::SRange::Serialize(std::ostream& os) const {
    449  os << std::setprecision(4) << std::fixed;
    450  if (discreteValues.empty()) {
    451    os << "[" << min << "-" << max << "]";
    452  } else if (discreteValues.size() == 1) {
    453    os << discreteValues.front();
    454  } else {
    455    os << "[";
    456    SkipFirstDelimiter comma(",");
    457    for (auto value : discreteValues) {
    458      os << comma << value;
    459    }
    460    os << "]";
    461  }
    462 }
    463 
    464 void SdpImageattrAttributeList::PRange::Serialize(std::ostream& os) const {
    465  os << std::setprecision(4) << std::fixed;
    466  os << "[" << min << "-" << max << "]";
    467 }
    468 
    469 static std::string ParseKey(std::istream& is, std::string* error) {
    470  std::string token = ParseToken(is, "=", error);
    471  if (!SkipChar(is, '=', error)) {
    472    return "";
    473  }
    474  return token;
    475 }
    476 
    477 static bool SkipBraces(std::istream& is, std::string* error) {
    478  if (PeekChar(is, error) != '[') {
    479    *error = "Expected \'[\'";
    480    return false;
    481  }
    482 
    483  size_t braceCount = 0;
    484  do {
    485    switch (PeekChar(is, error)) {
    486      case '[':
    487        ++braceCount;
    488        break;
    489      case ']':
    490        --braceCount;
    491        break;
    492      default:
    493        break;
    494    }
    495    is.get();
    496  } while (braceCount && is);
    497 
    498  if (!is) {
    499    *error = "Expected closing brace";
    500    return false;
    501  }
    502 
    503  return true;
    504 }
    505 
    506 // Assumptions:
    507 // 1. If the value contains '[' or ']', they are balanced.
    508 // 2. The value contains no ',' outside of brackets.
    509 static bool SkipValue(std::istream& is, std::string* error) {
    510  while (is) {
    511    switch (PeekChar(is, error)) {
    512      case ',':
    513      case ']':
    514        return true;
    515      case '[':
    516        if (!SkipBraces(is, error)) {
    517          return false;
    518        }
    519        break;
    520      default:
    521        is.get();
    522    }
    523  }
    524 
    525  *error = "No closing \']\' on set";
    526  return false;
    527 }
    528 
    529 bool SdpImageattrAttributeList::Set::Parse(std::istream& is,
    530                                           std::string* error) {
    531  if (!SkipChar(is, '[', error)) {
    532    return false;
    533  }
    534 
    535  if (ParseKey(is, error) != "x") {
    536    *error = "Expected x=";
    537    return false;
    538  }
    539 
    540  if (!xRange.Parse(is, error)) {
    541    return false;
    542  }
    543 
    544  if (!SkipChar(is, ',', error)) {
    545    return false;
    546  }
    547 
    548  if (ParseKey(is, error) != "y") {
    549    *error = "Expected y=";
    550    return false;
    551  }
    552 
    553  if (!yRange.Parse(is, error)) {
    554    return false;
    555  }
    556 
    557  qValue = 0.5f;  // default
    558 
    559  bool gotSar = false;
    560  bool gotPar = false;
    561  bool gotQ = false;
    562 
    563  while (SkipChar(is, ',', error)) {
    564    std::string key = ParseKey(is, error);
    565    if (key.empty()) {
    566      *error = "Expected key-value";
    567      return false;
    568    }
    569 
    570    if (key == "sar") {
    571      if (gotSar) {
    572        *error = "Extra sar parameter";
    573        return false;
    574      }
    575      gotSar = true;
    576      if (!sRange.Parse(is, error)) {
    577        return false;
    578      }
    579    } else if (key == "par") {
    580      if (gotPar) {
    581        *error = "Extra par parameter";
    582        return false;
    583      }
    584      gotPar = true;
    585      if (!pRange.Parse(is, error)) {
    586        return false;
    587      }
    588    } else if (key == "q") {
    589      if (gotQ) {
    590        *error = "Extra q parameter";
    591        return false;
    592      }
    593      gotQ = true;
    594      if (!GetQValue(is, &qValue, error)) {
    595        return false;
    596      }
    597    } else {
    598      if (!SkipValue(is, error)) {
    599        return false;
    600      }
    601    }
    602  }
    603 
    604  return SkipChar(is, ']', error);
    605 }
    606 
    607 void SdpImageattrAttributeList::Set::Serialize(std::ostream& os) const {
    608  os << "[x=";
    609  xRange.Serialize(os);
    610  os << ",y=";
    611  yRange.Serialize(os);
    612  if (sRange.IsSet()) {
    613    os << ",sar=";
    614    sRange.Serialize(os);
    615  }
    616  if (pRange.IsSet()) {
    617    os << ",par=";
    618    pRange.Serialize(os);
    619  }
    620  if (qValue >= 0) {
    621    os << std::setprecision(2) << std::fixed << ",q=" << qValue;
    622  }
    623  os << "]";
    624 }
    625 
    626 bool SdpImageattrAttributeList::Imageattr::ParseSets(std::istream& is,
    627                                                     std::string* error) {
    628  std::string type = ParseToken(is, " \t", error);
    629 
    630  bool* isAll = nullptr;
    631  std::vector<Set>* sets = nullptr;
    632 
    633  if (type == "send") {
    634    isAll = &sendAll;
    635    sets = &sendSets;
    636  } else if (type == "recv") {
    637    isAll = &recvAll;
    638    sets = &recvSets;
    639  } else {
    640    *error = "Unknown type, must be either send or recv";
    641    return false;
    642  }
    643 
    644  if (*isAll || !sets->empty()) {
    645    *error = "Multiple send or recv set lists";
    646    return false;
    647  }
    648 
    649  is >> std::ws;
    650  if (SkipChar(is, '*', error)) {
    651    *isAll = true;
    652    return true;
    653  }
    654 
    655  do {
    656    Set set;
    657    if (!set.Parse(is, error)) {
    658      return false;
    659    }
    660 
    661    sets->push_back(set);
    662    is >> std::ws;
    663  } while (PeekChar(is, error) == '[');
    664 
    665  return true;
    666 }
    667 
    668 bool SdpImageattrAttributeList::Imageattr::Parse(std::istream& is,
    669                                                 std::string* error) {
    670  if (!SkipChar(is, '*', error)) {
    671    uint16_t value;
    672    if (!GetUnsigned<uint16_t>(is, 0, UINT16_MAX, &value, error)) {
    673      return false;
    674    }
    675    pt = Some(value);
    676  }
    677 
    678  is >> std::ws;
    679  if (!ParseSets(is, error)) {
    680    return false;
    681  }
    682 
    683  // There might be a second one
    684  is >> std::ws;
    685  if (is.eof()) {
    686    return true;
    687  }
    688 
    689  if (!ParseSets(is, error)) {
    690    return false;
    691  }
    692 
    693  is >> std::ws;
    694  if (!is.eof()) {
    695    *error = "Trailing characters";
    696    return false;
    697  }
    698 
    699  return true;
    700 }
    701 
    702 void SdpImageattrAttributeList::Imageattr::Serialize(std::ostream& os) const {
    703  if (pt.isSome()) {
    704    os << *pt;
    705  } else {
    706    os << "*";
    707  }
    708 
    709  if (sendAll) {
    710    os << " send *";
    711  } else if (!sendSets.empty()) {
    712    os << " send";
    713    for (auto& set : sendSets) {
    714      os << " ";
    715      set.Serialize(os);
    716    }
    717  }
    718 
    719  if (recvAll) {
    720    os << " recv *";
    721  } else if (!recvSets.empty()) {
    722    os << " recv";
    723    for (auto& set : recvSets) {
    724      os << " ";
    725      set.Serialize(os);
    726    }
    727  }
    728 }
    729 
    730 void SdpImageattrAttributeList::Serialize(std::ostream& os) const {
    731  for (auto& imageattr : mImageattrs) {
    732    os << "a=" << mType << ":";
    733    imageattr.Serialize(os);
    734    os << CRLF;
    735  }
    736 }
    737 
    738 bool SdpImageattrAttributeList::PushEntry(const std::string& raw,
    739                                          std::string* error,
    740                                          size_t* errorPos) {
    741  std::istringstream is(raw);
    742 
    743  Imageattr imageattr;
    744  if (!imageattr.Parse(is, error)) {
    745    is.clear();
    746    *errorPos = is.tellg();
    747    return false;
    748  }
    749 
    750  mImageattrs.push_back(imageattr);
    751  return true;
    752 }
    753 
    754 void SdpMsidAttributeList::Serialize(std::ostream& os) const {
    755  for (auto i = mMsids.begin(); i != mMsids.end(); ++i) {
    756    os << "a=" << mType << ":" << i->identifier;
    757    if (i->appdata.length()) {
    758      os << " " << i->appdata;
    759    }
    760    os << CRLF;
    761  }
    762 }
    763 
    764 void SdpMsidSemanticAttributeList::Serialize(std::ostream& os) const {
    765  for (auto i = mMsidSemantics.begin(); i != mMsidSemantics.end(); ++i) {
    766    os << "a=" << mType << ":" << i->semantic;
    767    for (auto j = i->msids.begin(); j != i->msids.end(); ++j) {
    768      os << " " << *j;
    769    }
    770    os << CRLF;
    771  }
    772 }
    773 
    774 void SdpRemoteCandidatesAttribute::Serialize(std::ostream& os) const {
    775  if (mCandidates.empty()) {
    776    return;
    777  }
    778 
    779  os << "a=" << mType;
    780  for (auto i = mCandidates.begin(); i != mCandidates.end(); i++) {
    781    os << (i == mCandidates.begin() ? ":" : " ") << i->id << " " << i->address
    782       << " " << i->port;
    783  }
    784  os << CRLF;
    785 }
    786 
    787 // Remove this function. See Bug 1469702
    788 bool SdpRidAttributeList::Rid::ParseParameters(std::istream& is,
    789                                               std::string* error) {
    790  if (!PeekChar(is, error)) {
    791    // No parameters
    792    return true;
    793  }
    794 
    795  do {
    796    is >> std::ws;
    797    std::string key = ParseKey(is, error);
    798    if (key.empty()) {
    799      return false;  // Illegal trailing cruft
    800    }
    801 
    802    // This allows pt= to appear anywhere, instead of only at the beginning, but
    803    // this ends up being significantly less code.
    804    if (key == "pt") {
    805      if (!ParseFormats(is, error)) {
    806        return false;
    807      }
    808    } else if (key == "max-width") {
    809      if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxWidth,
    810                                 error)) {
    811        return false;
    812      }
    813    } else if (key == "max-height") {
    814      if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxHeight,
    815                                 error)) {
    816        return false;
    817      }
    818    } else if (key == "max-fps") {
    819      uint32_t maxFps;
    820      if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &maxFps, error)) {
    821        return false;
    822      }
    823      constraints.maxFps = Some(maxFps);
    824    } else if (key == "max-fs") {
    825      if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxFs,
    826                                 error)) {
    827        return false;
    828      }
    829    } else if (key == "max-br") {
    830      if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxBr,
    831                                 error)) {
    832        return false;
    833      }
    834    } else if (key == "max-pps") {
    835      if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxPps,
    836                                 error)) {
    837        return false;
    838      }
    839    } else if (key == "depend") {
    840      if (!ParseDepend(is, error)) {
    841        return false;
    842      }
    843    } else {
    844      (void)ParseToken(is, ";", error);
    845    }
    846  } while (SkipChar(is, ';', error));
    847  return true;
    848 }
    849 
    850 // Remove this function. See Bug 1469702
    851 bool SdpRidAttributeList::Rid::ParseDepend(std::istream& is,
    852                                           std::string* error) {
    853  do {
    854    std::string id = ParseToken(is, ",;", error);
    855    if (id.empty()) {
    856      return false;
    857    }
    858    dependIds.push_back(id);
    859  } while (SkipChar(is, ',', error));
    860 
    861  return true;
    862 }
    863 
    864 // Remove this function. See Bug 1469702
    865 bool SdpRidAttributeList::Rid::ParseFormats(std::istream& is,
    866                                            std::string* error) {
    867  do {
    868    uint16_t fmt;
    869    if (!GetUnsigned<uint16_t>(is, 0, 127, &fmt, error)) {
    870      return false;
    871    }
    872    formats.push_back(fmt);
    873  } while (SkipChar(is, ',', error));
    874 
    875  return true;
    876 }
    877 
    878 void SdpRidAttributeList::Rid::SerializeParameters(std::ostream& os) const {
    879  if (!HasParameters()) {
    880    return;
    881  }
    882 
    883  os << " ";
    884 
    885  SkipFirstDelimiter semic(";");
    886 
    887  if (!formats.empty()) {
    888    os << semic << "pt=";
    889    SkipFirstDelimiter comma(",");
    890    for (uint16_t fmt : formats) {
    891      os << comma << fmt;
    892    }
    893  }
    894 
    895  if (constraints.maxWidth) {
    896    os << semic << "max-width=" << constraints.maxWidth;
    897  }
    898 
    899  if (constraints.maxHeight) {
    900    os << semic << "max-height=" << constraints.maxHeight;
    901  }
    902 
    903  if (constraints.maxFps) {
    904    os << semic << "max-fps=" << constraints.maxFps;
    905  }
    906 
    907  if (constraints.maxFs) {
    908    os << semic << "max-fs=" << constraints.maxFs;
    909  }
    910 
    911  if (constraints.maxBr) {
    912    os << semic << "max-br=" << constraints.maxBr;
    913  }
    914 
    915  if (constraints.maxPps) {
    916    os << semic << "max-pps=" << constraints.maxPps;
    917  }
    918 
    919  if (!dependIds.empty()) {
    920    os << semic << "depend=";
    921    SkipFirstDelimiter comma(",");
    922    for (const std::string& id : dependIds) {
    923      os << comma << id;
    924    }
    925  }
    926 }
    927 
    928 // Remove this function. See Bug 1469702
    929 bool SdpRidAttributeList::Rid::Parse(std::istream& is, std::string* error) {
    930  id = ParseToken(is, " ", error);
    931  if (!CheckRidValidity(id, error)) {
    932    return false;
    933  }
    934 
    935  is >> std::ws;
    936  std::string directionToken = ParseToken(is, " ", error);
    937  if (directionToken == "send") {
    938    direction = sdp::kSend;
    939  } else if (directionToken == "recv") {
    940    direction = sdp::kRecv;
    941  } else {
    942    *error = "Invalid direction, must be either send or recv";
    943    return false;
    944  }
    945 
    946  return ParseParameters(is, error);
    947 }
    948 
    949 static std::bitset<256> GetAllowedRidCharacters() {
    950  // From RFC 8851:
    951  // rid-id            = 1*(alpha-numeric / "-" / "_")
    952  std::bitset<256> result;
    953  for (unsigned char c = 'a'; c <= 'z'; ++c) {
    954    result.set(c);
    955  }
    956  for (unsigned char c = 'A'; c <= 'Z'; ++c) {
    957    result.set(c);
    958  }
    959  for (unsigned char c = '0'; c <= '9'; ++c) {
    960    result.set(c);
    961  }
    962  // NOTE: RFC 8851 says these are allowed, but RFC 8852 says they are not
    963  // https://www.rfc-editor.org/errata/eid7132
    964  // result.set('-');
    965  // result.set('_');
    966  return result;
    967 }
    968 
    969 /* static */
    970 bool SdpRidAttributeList::CheckRidValidity(const std::string& aRid,
    971                                           std::string* aError) {
    972  if (aRid.empty()) {
    973    *aError = "Rid must be non-empty (according to RFC 8851)";
    974    return false;
    975  }
    976 
    977  // We need to check against a maximum length, but that is nowhere
    978  // specified in webrtc-pc right now.
    979  if (aRid.size() > 255) {
    980    *aError = "Rid can be at most 255 characters long (according to RFC 8852)";
    981    return false;
    982  }
    983 
    984  // TODO: Right now, if the rid is longer than kMaxRidLength, we don't treat it
    985  // as a parse error, since the grammar does not have this restriction.
    986  // Instead, our JSEP code ignores rids that exceed this limit. However, there
    987  // is a possibility that the IETF grammar (in RFC 8852) will change the limit
    988  // from 255 to 16, in which case we will need to revise this code.
    989 
    990  static const std::bitset<256> allowed = GetAllowedRidCharacters();
    991  for (unsigned char c : aRid) {
    992    if (!allowed[c]) {
    993      *aError =
    994          "Rid can contain only alphanumeric characters (according to RFC "
    995          "8852)";
    996      return false;
    997    }
    998  }
    999 
   1000  return true;
   1001 }
   1002 
   1003 // This can be overridden if necessary
   1004 size_t SdpRidAttributeList::kMaxRidLength = 255;
   1005 
   1006 void SdpRidAttributeList::Rid::Serialize(std::ostream& os) const {
   1007  os << id << " " << direction;
   1008  SerializeParameters(os);
   1009 }
   1010 
   1011 bool SdpRidAttributeList::Rid::HasFormat(const std::string& format) const {
   1012  if (formats.empty()) {
   1013    return true;
   1014  }
   1015 
   1016  uint16_t formatAsInt;
   1017  if (!SdpHelper::GetPtAsInt(format, &formatAsInt)) {
   1018    return false;
   1019  }
   1020 
   1021  return (std::find(formats.begin(), formats.end(), formatAsInt) !=
   1022          formats.end());
   1023 }
   1024 
   1025 void SdpRidAttributeList::Serialize(std::ostream& os) const {
   1026  for (const Rid& rid : mRids) {
   1027    os << "a=" << mType << ":";
   1028    rid.Serialize(os);
   1029    os << CRLF;
   1030  }
   1031 }
   1032 
   1033 // Remove this function. See Bug 1469702
   1034 bool SdpRidAttributeList::PushEntry(const std::string& raw, std::string* error,
   1035                                    size_t* errorPos) {
   1036  std::istringstream is(raw);
   1037 
   1038  Rid rid;
   1039  if (!rid.Parse(is, error)) {
   1040    is.clear();
   1041    *errorPos = is.tellg();
   1042    return false;
   1043  }
   1044 
   1045  mRids.push_back(rid);
   1046  return true;
   1047 }
   1048 
   1049 void SdpRidAttributeList::PushEntry(const std::string& id, sdp::Direction dir,
   1050                                    const std::vector<uint16_t>& formats,
   1051                                    const VideoEncodingConstraints& constraints,
   1052                                    const std::vector<std::string>& dependIds) {
   1053  SdpRidAttributeList::Rid rid;
   1054 
   1055  rid.id = id;
   1056  rid.direction = dir;
   1057  rid.formats = formats;
   1058  rid.constraints = constraints;
   1059  rid.dependIds = dependIds;
   1060 
   1061  mRids.push_back(std::move(rid));
   1062 }
   1063 
   1064 void SdpRtcpAttribute::Serialize(std::ostream& os) const {
   1065  os << "a=" << mType << ":" << mPort;
   1066  if (!mAddress.empty()) {
   1067    os << " " << mNetType << " " << mAddrType << " " << mAddress;
   1068  }
   1069  os << CRLF;
   1070 }
   1071 
   1072 const char* SdpRtcpFbAttributeList::pli = "pli";
   1073 const char* SdpRtcpFbAttributeList::sli = "sli";
   1074 const char* SdpRtcpFbAttributeList::rpsi = "rpsi";
   1075 const char* SdpRtcpFbAttributeList::app = "app";
   1076 
   1077 const char* SdpRtcpFbAttributeList::fir = "fir";
   1078 const char* SdpRtcpFbAttributeList::tmmbr = "tmmbr";
   1079 const char* SdpRtcpFbAttributeList::tstr = "tstr";
   1080 const char* SdpRtcpFbAttributeList::vbcm = "vbcm";
   1081 
   1082 void SdpRtcpFbAttributeList::Serialize(std::ostream& os) const {
   1083  for (auto i = mFeedbacks.begin(); i != mFeedbacks.end(); ++i) {
   1084    os << "a=" << mType << ":" << i->pt << " " << i->type;
   1085    if (i->parameter.length()) {
   1086      os << " " << i->parameter;
   1087      if (i->extra.length()) {
   1088        os << " " << i->extra;
   1089      }
   1090    }
   1091    os << CRLF;
   1092  }
   1093 }
   1094 
   1095 static bool ShouldSerializeChannels(SdpRtpmapAttributeList::CodecType type) {
   1096  switch (type) {
   1097    case SdpRtpmapAttributeList::kOpus:
   1098    case SdpRtpmapAttributeList::kG722:
   1099      return true;
   1100    case SdpRtpmapAttributeList::kPCMU:
   1101    case SdpRtpmapAttributeList::kPCMA:
   1102    case SdpRtpmapAttributeList::kVP8:
   1103    case SdpRtpmapAttributeList::kVP9:
   1104    case SdpRtpmapAttributeList::kiLBC:
   1105    case SdpRtpmapAttributeList::kiSAC:
   1106    case SdpRtpmapAttributeList::kH264:
   1107    case SdpRtpmapAttributeList::kAV1:
   1108    case SdpRtpmapAttributeList::kRed:
   1109    case SdpRtpmapAttributeList::kUlpfec:
   1110    case SdpRtpmapAttributeList::kTelephoneEvent:
   1111    case SdpRtpmapAttributeList::kRtx:
   1112      return false;
   1113    case SdpRtpmapAttributeList::kOtherCodec:
   1114      return true;
   1115  }
   1116  MOZ_CRASH();
   1117 }
   1118 
   1119 void SdpRtpmapAttributeList::Serialize(std::ostream& os) const {
   1120  for (auto i = mRtpmaps.begin(); i != mRtpmaps.end(); ++i) {
   1121    os << "a=" << mType << ":" << i->pt << " " << i->name << "/" << i->clock;
   1122    if (i->channels && ShouldSerializeChannels(i->codec)) {
   1123      os << "/" << i->channels;
   1124    }
   1125    os << CRLF;
   1126  }
   1127 }
   1128 
   1129 void SdpSctpmapAttributeList::Serialize(std::ostream& os) const {
   1130  for (auto i = mSctpmaps.begin(); i != mSctpmaps.end(); ++i) {
   1131    os << "a=" << mType << ":" << i->pt << " " << i->name << " " << i->streams
   1132       << CRLF;
   1133  }
   1134 }
   1135 
   1136 void SdpSetupAttribute::Serialize(std::ostream& os) const {
   1137  os << "a=" << mType << ":" << mRole << CRLF;
   1138 }
   1139 
   1140 void SdpSimulcastAttribute::Version::Serialize(std::ostream& os) const {
   1141  SkipFirstDelimiter comma(",");
   1142  for (const Encoding& choice : choices) {
   1143    os << comma;
   1144    if (choice.paused) {
   1145      os << '~';
   1146    }
   1147    os << choice.rid;
   1148  }
   1149 }
   1150 
   1151 bool SdpSimulcastAttribute::Version::Parse(std::istream& is,
   1152                                           std::string* error) {
   1153  do {
   1154    bool paused = SkipChar(is, '~', error);
   1155    std::string value = ParseToken(is, ",; ", error);
   1156    if (value.empty()) {
   1157      *error = "Missing rid";
   1158      return false;
   1159    }
   1160    if (!SdpRidAttributeList::CheckRidValidity(value, error)) {
   1161      return false;
   1162    }
   1163    choices.push_back(Encoding(value, paused));
   1164  } while (SkipChar(is, ',', error));
   1165 
   1166  return true;
   1167 }
   1168 
   1169 void SdpSimulcastAttribute::Versions::Serialize(std::ostream& os) const {
   1170  SkipFirstDelimiter semic(";");
   1171  for (const Version& version : *this) {
   1172    if (!version.IsSet()) {
   1173      continue;
   1174    }
   1175    os << semic;
   1176    version.Serialize(os);
   1177  }
   1178 }
   1179 
   1180 bool SdpSimulcastAttribute::Versions::Parse(std::istream& is,
   1181                                            std::string* error) {
   1182  do {
   1183    Version version;
   1184    if (!version.Parse(is, error)) {
   1185      return false;
   1186    }
   1187    push_back(version);
   1188  } while (SkipChar(is, ';', error));
   1189 
   1190  return true;
   1191 }
   1192 
   1193 void SdpSimulcastAttribute::Serialize(std::ostream& os) const {
   1194  MOZ_ASSERT(sendVersions.IsSet() || recvVersions.IsSet());
   1195 
   1196  os << "a=" << mType << ":";
   1197 
   1198  if (sendVersions.IsSet()) {
   1199    os << "send ";
   1200    sendVersions.Serialize(os);
   1201  }
   1202 
   1203  if (recvVersions.IsSet()) {
   1204    if (sendVersions.IsSet()) {
   1205      os << " ";
   1206    }
   1207    os << "recv ";
   1208    recvVersions.Serialize(os);
   1209  }
   1210 
   1211  os << CRLF;
   1212 }
   1213 
   1214 bool SdpSimulcastAttribute::Parse(std::istream& is, std::string* error) {
   1215  bool gotRecv = false;
   1216  bool gotSend = false;
   1217 
   1218  while (true) {
   1219    is >> std::ws;
   1220    std::string token = ParseToken(is, " \t", error);
   1221    if (token.empty()) {
   1222      break;
   1223    }
   1224 
   1225    if (token == "send") {
   1226      if (gotSend) {
   1227        *error = "Already got a send list";
   1228        return false;
   1229      }
   1230      gotSend = true;
   1231 
   1232      is >> std::ws;
   1233      if (!sendVersions.Parse(is, error)) {
   1234        return false;
   1235      }
   1236    } else if (token == "recv") {
   1237      if (gotRecv) {
   1238        *error = "Already got a recv list";
   1239        return false;
   1240      }
   1241      gotRecv = true;
   1242 
   1243      is >> std::ws;
   1244      if (!recvVersions.Parse(is, error)) {
   1245        return false;
   1246      }
   1247    } else {
   1248      *error = "Type must be either 'send' or 'recv'";
   1249      return false;
   1250    }
   1251  }
   1252 
   1253  if (!gotSend && !gotRecv) {
   1254    *error = "Empty simulcast attribute";
   1255    return false;
   1256  }
   1257 
   1258  return true;
   1259 }
   1260 
   1261 void SdpSsrcAttributeList::Serialize(std::ostream& os) const {
   1262  for (auto i = mSsrcs.begin(); i != mSsrcs.end(); ++i) {
   1263    os << "a=" << mType << ":" << i->ssrc << " " << i->attribute << CRLF;
   1264  }
   1265 }
   1266 
   1267 void SdpSsrcGroupAttributeList::Serialize(std::ostream& os) const {
   1268  for (auto i = mSsrcGroups.begin(); i != mSsrcGroups.end(); ++i) {
   1269    os << "a=" << mType << ":" << i->semantics;
   1270    for (auto j = i->ssrcs.begin(); j != i->ssrcs.end(); ++j) {
   1271      os << " " << (*j);
   1272    }
   1273    os << CRLF;
   1274  }
   1275 }
   1276 
   1277 void SdpMultiStringAttribute::Serialize(std::ostream& os) const {
   1278  for (auto i = mValues.begin(); i != mValues.end(); ++i) {
   1279    os << "a=" << mType << ":" << *i << CRLF;
   1280  }
   1281 }
   1282 
   1283 void SdpOptionsAttribute::Serialize(std::ostream& os) const {
   1284  if (mValues.empty()) {
   1285    return;
   1286  }
   1287 
   1288  os << "a=" << mType << ":";
   1289 
   1290  for (auto i = mValues.begin(); i != mValues.end(); ++i) {
   1291    if (i != mValues.begin()) {
   1292      os << " ";
   1293    }
   1294    os << *i;
   1295  }
   1296  os << CRLF;
   1297 }
   1298 
   1299 void SdpOptionsAttribute::Load(const std::string& value) {
   1300  size_t start = 0;
   1301  size_t end = value.find(' ');
   1302  while (end != std::string::npos) {
   1303    PushEntry(value.substr(start, end));
   1304    start = end + 1;
   1305    end = value.find(' ', start);
   1306  }
   1307  PushEntry(value.substr(start));
   1308 }
   1309 
   1310 void SdpFlagAttribute::Serialize(std::ostream& os) const {
   1311  os << "a=" << mType << CRLF;
   1312 }
   1313 
   1314 void SdpStringAttribute::Serialize(std::ostream& os) const {
   1315  os << "a=" << mType << ":" << mValue << CRLF;
   1316 }
   1317 
   1318 void SdpNumberAttribute::Serialize(std::ostream& os) const {
   1319  os << "a=" << mType << ":" << mValue << CRLF;
   1320 }
   1321 
   1322 bool SdpAttribute::IsAllowedAtMediaLevel(AttributeType type) {
   1323  switch (type) {
   1324    case kBundleOnlyAttribute:
   1325      return true;
   1326    case kCandidateAttribute:
   1327      return true;
   1328    case kConnectionAttribute:
   1329      return true;
   1330    case kDirectionAttribute:
   1331      return true;
   1332    case kDtlsMessageAttribute:
   1333      return false;
   1334    case kEndOfCandidatesAttribute:
   1335      return true;
   1336    case kExtmapAttribute:
   1337      return true;
   1338    case kExtmapAllowMixedAttribute:
   1339      return true;
   1340    case kFingerprintAttribute:
   1341      return true;
   1342    case kFmtpAttribute:
   1343      return true;
   1344    case kGroupAttribute:
   1345      return false;
   1346    case kIceLiteAttribute:
   1347      return false;
   1348    case kIceMismatchAttribute:
   1349      return true;
   1350    // RFC 5245 says this is session-level only, but
   1351    // draft-ietf-mmusic-ice-sip-sdp-03 updates this to allow at the media
   1352    // level.
   1353    case kIceOptionsAttribute:
   1354      return true;
   1355    case kIcePwdAttribute:
   1356      return true;
   1357    case kIceUfragAttribute:
   1358      return true;
   1359    case kIdentityAttribute:
   1360      return false;
   1361    case kImageattrAttribute:
   1362      return true;
   1363    case kLabelAttribute:
   1364      return true;
   1365    case kMaxptimeAttribute:
   1366      return true;
   1367    case kMidAttribute:
   1368      return true;
   1369    case kMsidAttribute:
   1370      return true;
   1371    case kMsidSemanticAttribute:
   1372      return false;
   1373    case kPtimeAttribute:
   1374      return true;
   1375    case kRemoteCandidatesAttribute:
   1376      return true;
   1377    case kRidAttribute:
   1378      return true;
   1379    case kRtcpAttribute:
   1380      return true;
   1381    case kRtcpFbAttribute:
   1382      return true;
   1383    case kRtcpMuxAttribute:
   1384      return true;
   1385    case kRtcpRsizeAttribute:
   1386      return true;
   1387    case kRtpmapAttribute:
   1388      return true;
   1389    case kSctpmapAttribute:
   1390      return true;
   1391    case kSetupAttribute:
   1392      return true;
   1393    case kSimulcastAttribute:
   1394      return true;
   1395    case kSsrcAttribute:
   1396      return true;
   1397    case kSsrcGroupAttribute:
   1398      return true;
   1399    case kSctpPortAttribute:
   1400      return true;
   1401    case kMaxMessageSizeAttribute:
   1402      return true;
   1403  }
   1404  MOZ_CRASH("Unknown attribute type");
   1405 }
   1406 
   1407 bool SdpAttribute::IsAllowedAtSessionLevel(AttributeType type) {
   1408  switch (type) {
   1409    case kBundleOnlyAttribute:
   1410      return false;
   1411    case kCandidateAttribute:
   1412      return false;
   1413    case kConnectionAttribute:
   1414      return true;
   1415    case kDirectionAttribute:
   1416      return true;
   1417    case kDtlsMessageAttribute:
   1418      return true;
   1419    case kEndOfCandidatesAttribute:
   1420      return true;
   1421    case kExtmapAttribute:
   1422      return true;
   1423    case kExtmapAllowMixedAttribute:
   1424      return true;
   1425    case kFingerprintAttribute:
   1426      return true;
   1427    case kFmtpAttribute:
   1428      return false;
   1429    case kGroupAttribute:
   1430      return true;
   1431    case kIceLiteAttribute:
   1432      return true;
   1433    case kIceMismatchAttribute:
   1434      return false;
   1435    case kIceOptionsAttribute:
   1436      return true;
   1437    case kIcePwdAttribute:
   1438      return true;
   1439    case kIceUfragAttribute:
   1440      return true;
   1441    case kIdentityAttribute:
   1442      return true;
   1443    case kImageattrAttribute:
   1444      return false;
   1445    case kLabelAttribute:
   1446      return false;
   1447    case kMaxptimeAttribute:
   1448      return false;
   1449    case kMidAttribute:
   1450      return false;
   1451    case kMsidSemanticAttribute:
   1452      return true;
   1453    case kMsidAttribute:
   1454      return false;
   1455    case kPtimeAttribute:
   1456      return false;
   1457    case kRemoteCandidatesAttribute:
   1458      return false;
   1459    case kRidAttribute:
   1460      return false;
   1461    case kRtcpAttribute:
   1462      return false;
   1463    case kRtcpFbAttribute:
   1464      return false;
   1465    case kRtcpMuxAttribute:
   1466      return false;
   1467    case kRtcpRsizeAttribute:
   1468      return false;
   1469    case kRtpmapAttribute:
   1470      return false;
   1471    case kSctpmapAttribute:
   1472      return false;
   1473    case kSetupAttribute:
   1474      return true;
   1475    case kSimulcastAttribute:
   1476      return false;
   1477    case kSsrcAttribute:
   1478      return false;
   1479    case kSsrcGroupAttribute:
   1480      return false;
   1481    case kSctpPortAttribute:
   1482      return false;
   1483    case kMaxMessageSizeAttribute:
   1484      return false;
   1485  }
   1486  MOZ_CRASH("Unknown attribute type");
   1487 }
   1488 
   1489 const std::string SdpAttribute::GetAttributeTypeString(AttributeType type) {
   1490  switch (type) {
   1491    case kBundleOnlyAttribute:
   1492      return "bundle-only";
   1493    case kCandidateAttribute:
   1494      return "candidate";
   1495    case kConnectionAttribute:
   1496      return "connection";
   1497    case kDtlsMessageAttribute:
   1498      return "dtls-message";
   1499    case kEndOfCandidatesAttribute:
   1500      return "end-of-candidates";
   1501    case kExtmapAttribute:
   1502      return "extmap";
   1503    case kExtmapAllowMixedAttribute:
   1504      return "extmap-allow-mixed";
   1505    case kFingerprintAttribute:
   1506      return "fingerprint";
   1507    case kFmtpAttribute:
   1508      return "fmtp";
   1509    case kGroupAttribute:
   1510      return "group";
   1511    case kIceLiteAttribute:
   1512      return "ice-lite";
   1513    case kIceMismatchAttribute:
   1514      return "ice-mismatch";
   1515    case kIceOptionsAttribute:
   1516      return "ice-options";
   1517    case kIcePwdAttribute:
   1518      return "ice-pwd";
   1519    case kIceUfragAttribute:
   1520      return "ice-ufrag";
   1521    case kIdentityAttribute:
   1522      return "identity";
   1523    case kImageattrAttribute:
   1524      return "imageattr";
   1525    case kLabelAttribute:
   1526      return "label";
   1527    case kMaxptimeAttribute:
   1528      return "maxptime";
   1529    case kMidAttribute:
   1530      return "mid";
   1531    case kMsidAttribute:
   1532      return "msid";
   1533    case kMsidSemanticAttribute:
   1534      return "msid-semantic";
   1535    case kPtimeAttribute:
   1536      return "ptime";
   1537    case kRemoteCandidatesAttribute:
   1538      return "remote-candidates";
   1539    case kRidAttribute:
   1540      return "rid";
   1541    case kRtcpAttribute:
   1542      return "rtcp";
   1543    case kRtcpFbAttribute:
   1544      return "rtcp-fb";
   1545    case kRtcpMuxAttribute:
   1546      return "rtcp-mux";
   1547    case kRtcpRsizeAttribute:
   1548      return "rtcp-rsize";
   1549    case kRtpmapAttribute:
   1550      return "rtpmap";
   1551    case kSctpmapAttribute:
   1552      return "sctpmap";
   1553    case kSetupAttribute:
   1554      return "setup";
   1555    case kSimulcastAttribute:
   1556      return "simulcast";
   1557    case kSsrcAttribute:
   1558      return "ssrc";
   1559    case kSsrcGroupAttribute:
   1560      return "ssrc-group";
   1561    case kSctpPortAttribute:
   1562      return "sctp-port";
   1563    case kMaxMessageSizeAttribute:
   1564      return "max-message-size";
   1565    case kDirectionAttribute:
   1566      MOZ_CRASH("kDirectionAttribute not valid here");
   1567  }
   1568  MOZ_CRASH("Unknown attribute type");
   1569 }
   1570 
   1571 }  // namespace mozilla