CapsuleParser.cpp (7654B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set sw=2 ts=8 et 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 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "CapsuleParser.h" 8 9 #include "CapsuleDecoder.h" 10 #include "mozilla/ScopeExit.h" 11 12 namespace mozilla::net { 13 14 bool CapsuleParserListener::OnCapsule(Capsule&& aParsed) { 15 mParsedCapsules.AppendElement(std::move(aParsed)); 16 return true; 17 } 18 19 void CapsuleParserListener::OnCapsuleParseFailure(nsresult aError) { 20 mError = Some(aError); 21 } 22 23 CapsuleParser::CapsuleParser(Listener* aListener) : mListener(aListener) {} 24 25 bool CapsuleParser::ProcessCapsuleData(const uint8_t* aData, uint32_t aCount) { 26 // Prevent reentrant calls: if we're already processing, just return. 27 if (mProcessing) { 28 return false; 29 } 30 mProcessing = true; 31 32 auto resetGuard = MakeScopeExit([&]() { mProcessing = false; }); 33 34 Span<const uint8_t> input; 35 if (!mBuffer.IsEmpty()) { 36 mBuffer.AppendElements(aData, aCount); 37 input = Span<const uint8_t>(mBuffer.Elements(), mBuffer.Length()); 38 } else { 39 input = Span<const uint8_t>(aData, aCount); 40 } 41 size_t pos = 0; 42 size_t length = input.Length(); 43 44 while (true) { 45 Span<const uint8_t> toProcess = input.Subspan(pos, length - pos); 46 auto result = ParseCapsuleData(toProcess); 47 if (result.isErr()) { 48 mBuffer.Clear(); 49 return false; 50 } 51 52 size_t processed = result.unwrap(); 53 if (processed == 0) { 54 if (mBuffer.IsEmpty()) { 55 // Store the remaining data in mBuffer. 56 mBuffer.AppendElements(toProcess.Elements(), toProcess.Length()); 57 } else { 58 // Simply remove the already processed data. 59 mBuffer.RemoveElementsAt(0, pos); 60 } 61 break; 62 } 63 64 pos += processed; 65 } 66 67 return true; 68 } 69 70 Result<size_t, nsresult> CapsuleParser::ParseCapsuleData( 71 Span<const uint8_t> aData) { 72 if (aData.IsEmpty()) { 73 return 0; 74 } 75 76 CapsuleDecoder decoder(aData.Elements(), aData.Length()); 77 auto type = decoder.DecodeVarint(); 78 79 if (!type) { 80 return 0; 81 } 82 83 CapsuleType capsuleType = static_cast<CapsuleType>(*type); 84 auto payloadLength = decoder.DecodeVarint(); 85 if (!payloadLength) { 86 return 0; 87 } 88 89 auto payload = decoder.Decode(*payloadLength); 90 if (!payload) { 91 return 0; 92 } 93 94 CapsuleDecoder payloadParser(payload->Elements(), payload->Length()); 95 auto result = 96 ParseCapsulePayload(payloadParser, capsuleType, payload->Length()); 97 if (result.isErr()) { 98 nsresult error = result.unwrapErr(); 99 mListener->OnCapsuleParseFailure(error); 100 return Err(error); 101 } 102 103 Capsule capsule = result.unwrap(); 104 if (!mListener->OnCapsule(std::move(capsule))) { 105 return Err(NS_ERROR_FAILURE); 106 } 107 108 return decoder.CurrentPos(); 109 } 110 111 Result<Capsule, nsresult> CapsuleParser::ParseCapsulePayload( 112 CapsuleDecoder& aDecoder, CapsuleType aType, size_t aPayloadLength) { 113 switch (aType) { 114 case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: { 115 if (aPayloadLength < 4) { 116 return Err(NS_ERROR_UNEXPECTED); 117 } 118 Maybe<uint32_t> status = aDecoder.DecodeUint<uint32_t>(); 119 if (!status) { 120 return Err(NS_ERROR_UNEXPECTED); 121 } 122 // https://www.ietf.org/archive/id/draft-ietf-webtrans-http2-10.html#section-6.12 123 // The reason MUST not exceed 1024 bytes. 124 if (aDecoder.BytesRemaining() > 1024) { 125 return Err(NS_ERROR_UNEXPECTED); 126 } 127 auto reason = aDecoder.Decode(aPayloadLength - 4); 128 if (!reason) { 129 return Err(NS_ERROR_UNEXPECTED); 130 } 131 return Capsule::CloseWebTransportSession( 132 *status, nsCString(reinterpret_cast<const char*>(reason->Elements()), 133 reason->Length())); 134 } 135 case CapsuleType::DRAIN_WEBTRANSPORT_SESSION: 136 break; 137 case CapsuleType::PADDING: 138 break; 139 case CapsuleType::WT_RESET_STREAM: { 140 auto id = aDecoder.DecodeVarint(); 141 if (!id) { 142 return Err(NS_ERROR_UNEXPECTED); 143 } 144 auto error = aDecoder.DecodeVarint(); 145 if (!error) { 146 return Err(NS_ERROR_UNEXPECTED); 147 } 148 auto size = aDecoder.DecodeVarint(); 149 if (!size) { 150 return Err(NS_ERROR_UNEXPECTED); 151 } 152 return Capsule::WebTransportResetStream(*error, *size, *id); 153 } 154 case CapsuleType::WT_STOP_SENDING: { 155 auto id = aDecoder.DecodeVarint(); 156 if (!id) { 157 return Err(NS_ERROR_UNEXPECTED); 158 } 159 auto error = aDecoder.DecodeVarint(); 160 if (!error) { 161 return Err(NS_ERROR_UNEXPECTED); 162 } 163 return Capsule::WebTransportStopSending(*error, *id); 164 } 165 case CapsuleType::WT_STREAM: { 166 auto id = aDecoder.DecodeVarint(); 167 if (!id) { 168 return Err(NS_ERROR_UNEXPECTED); 169 } 170 nsTArray<uint8_t> data(aDecoder.GetRemaining()); 171 return Capsule::WebTransportStreamData(*id, false, std::move(data)); 172 } 173 case CapsuleType::WT_STREAM_FIN: { 174 auto id = aDecoder.DecodeVarint(); 175 if (!id) { 176 return Err(NS_ERROR_UNEXPECTED); 177 } 178 nsTArray<uint8_t> data(aDecoder.GetRemaining()); 179 return Capsule::WebTransportStreamData(*id, true, std::move(data)); 180 } 181 case CapsuleType::WT_MAX_DATA: { 182 auto value = aDecoder.DecodeVarint(); 183 if (!value) { 184 return Err(NS_ERROR_UNEXPECTED); 185 } 186 return Capsule::WebTransportMaxData(*value); 187 } 188 case CapsuleType::WT_MAX_STREAM_DATA: { 189 auto id = aDecoder.DecodeVarint(); 190 if (!id) { 191 return Err(NS_ERROR_UNEXPECTED); 192 } 193 auto limit = aDecoder.DecodeVarint(); 194 if (!limit) { 195 return Err(NS_ERROR_UNEXPECTED); 196 } 197 return Capsule::WebTransportMaxStreamData(*limit, *id); 198 } 199 case CapsuleType::WT_MAX_STREAMS_BIDI: { 200 auto value = aDecoder.DecodeVarint(); 201 if (!value) { 202 return Err(NS_ERROR_UNEXPECTED); 203 } 204 return Capsule::WebTransportMaxStreams(*value, true); 205 } 206 case CapsuleType::WT_MAX_STREAMS_UNIDI: { 207 auto value = aDecoder.DecodeVarint(); 208 if (!value) { 209 return Err(NS_ERROR_UNEXPECTED); 210 } 211 return Capsule::WebTransportMaxStreams(*value, false); 212 } 213 case CapsuleType::WT_DATA_BLOCKED: { 214 auto limit = aDecoder.DecodeVarint(); 215 if (!limit) { 216 return Err(NS_ERROR_UNEXPECTED); 217 } 218 return Capsule::WebTransportDataBlocked(*limit); 219 } 220 case CapsuleType::WT_STREAM_DATA_BLOCKED: { 221 auto id = aDecoder.DecodeVarint(); 222 if (!id) { 223 return Err(NS_ERROR_UNEXPECTED); 224 } 225 auto limit = aDecoder.DecodeVarint(); 226 if (!limit) { 227 return Err(NS_ERROR_UNEXPECTED); 228 } 229 return Capsule::WebTransportStreamDataBlocked(*limit, *id); 230 } 231 case CapsuleType::WT_STREAMS_BLOCKED_BIDI: { 232 auto value = aDecoder.DecodeVarint(); 233 if (!value) { 234 return Err(NS_ERROR_UNEXPECTED); 235 } 236 return Capsule::WebTransportStreamsBlocked(*value, true); 237 } 238 case CapsuleType::WT_STREAMS_BLOCKED_UNIDI: { 239 auto value = aDecoder.DecodeVarint(); 240 if (!value) { 241 return Err(NS_ERROR_UNEXPECTED); 242 } 243 return Capsule::WebTransportStreamsBlocked(*value, false); 244 } 245 case CapsuleType::DATAGRAM: { 246 nsTArray<uint8_t> payload(aDecoder.GetRemaining()); 247 return Capsule::WebTransportDatagram(std::move(payload)); 248 } 249 default: 250 break; 251 } 252 253 return Capsule::Unknown(static_cast<uint64_t>(aType), 254 nsTArray<uint8_t>(aDecoder.GetRemaining())); 255 } 256 257 } // namespace mozilla::net