1use core::fmt;
5
6use crate::error::{EncodingError, FormError, Limit, Location, PolicyRefusal, SyntaxError};
7use crate::limits::{CHARSET_CAPTURE, EmptyFilePolicy, Limits, Policy, TokenPlacement};
8use crate::multipart::boundary::{Boundary, Hit};
9use crate::multipart::filename::FileName;
10use crate::multipart::header::{Areas, BlockScan, HeadFacts, Text, read_block};
11
12#[non_exhaustive]
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24pub enum PartKind {
25 Field,
27 File,
30}
31
32#[non_exhaustive]
49#[derive(Clone, Copy, Debug, PartialEq, Eq)]
50pub enum Charset {
51 Utf8,
53 Ascii,
55 Unspecified,
57 Other,
59}
60
61#[cfg(feature = "alloc")]
64pub(crate) fn declared_readable(declared: Option<&[u8]>) -> bool {
65 declared.is_none_or(|label| {
66 label.eq_ignore_ascii_case(b"utf-8") || label.eq_ignore_ascii_case(b"us-ascii")
67 })
68}
69
70impl Charset {
71 #[cfg(feature = "alloc")]
74 pub(crate) const fn readable(self, declared_ok: bool) -> bool {
75 match self {
76 Charset::Utf8 | Charset::Ascii => true,
77 Charset::Other => false,
78 Charset::Unspecified => declared_ok,
79 }
80 }
81
82 pub(crate) fn from_label(label: &[u8]) -> Charset {
83 if label.eq_ignore_ascii_case(b"utf-8") {
84 Charset::Utf8
85 } else if label.eq_ignore_ascii_case(b"us-ascii") {
86 Charset::Ascii
87 } else {
88 Charset::Other
89 }
90 }
91}
92
93#[non_exhaustive]
96#[derive(Debug)]
97pub struct MultipartStep<'p> {
98 pub consumed: usize,
101 pub event: MultipartEvent<'p>,
103}
104
105#[non_exhaustive]
113pub enum MultipartEvent<'p> {
114 PartStart(PartHead<'p>),
116 Data(&'p [u8]),
118 PartEnd(PartSummary),
120 End,
122 NeedInput,
124}
125
126impl fmt::Debug for MultipartEvent<'_> {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 match self {
129 Self::PartStart(head) => f.debug_tuple("PartStart").field(head).finish(),
130 Self::Data(bytes) => write!(f, "Data({} bytes)", bytes.len()),
131 Self::PartEnd(summary) => f.debug_tuple("PartEnd").field(summary).finish(),
132 Self::End => f.write_str("End"),
133 Self::NeedInput => f.write_str("NeedInput"),
134 }
135 }
136}
137
138#[non_exhaustive]
140#[derive(Clone, Copy, Debug, PartialEq, Eq)]
141pub struct PartSummary {
142 pub index: u32,
144 pub kind: PartKind,
146 pub bytes: u64,
148 pub skipped: bool,
150}
151
152#[derive(Clone, Copy)]
156pub struct PartHead<'p> {
157 index: u32,
158 block: &'p [u8],
159 name: &'p str,
160 name_raw: &'p [u8],
161 kind: PartKind,
162 filename: Option<FileName<'p>>,
163 content_type: Option<&'p [u8]>,
164 charset: Charset,
165}
166
167impl fmt::Debug for PartHead<'_> {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 f.debug_struct("PartHead")
170 .field("index", &self.index)
171 .field("name", &self.name)
172 .field("kind", &self.kind)
173 .field("filename", &self.filename)
174 .finish_non_exhaustive()
175 }
176}
177
178impl<'p> PartHead<'p> {
179 #[cfg(feature = "alloc")]
181 #[allow(clippy::too_many_arguments)]
182 pub(crate) const fn from_parts(
183 index: u32,
184 block: &'p [u8],
185 name: &'p str,
186 name_raw: &'p [u8],
187 kind: PartKind,
188 filename: Option<FileName<'p>>,
189 content_type: Option<&'p [u8]>,
190 charset: Charset,
191 ) -> Self {
192 Self {
193 index,
194 block,
195 name,
196 name_raw,
197 kind,
198 filename,
199 content_type,
200 charset,
201 }
202 }
203
204 #[cfg(feature = "alloc")]
206 pub(crate) const fn block(&self) -> &'p [u8] {
207 self.block
208 }
209
210 #[must_use]
212 pub const fn index(&self) -> u32 {
213 self.index
214 }
215
216 #[must_use]
219 pub const fn name(&self) -> &'p str {
220 self.name
221 }
222
223 #[must_use]
225 pub const fn name_raw(&self) -> &'p [u8] {
226 self.name_raw
227 }
228
229 #[must_use]
231 pub const fn kind(&self) -> PartKind {
232 self.kind
233 }
234
235 #[must_use]
237 pub const fn is_file(&self) -> bool {
238 matches!(self.kind, PartKind::File)
239 }
240
241 #[must_use]
243 pub const fn filename(&self) -> Option<FileName<'p>> {
244 self.filename
245 }
246
247 #[must_use]
249 pub fn content_type(&self) -> Option<ContentType<'p>> {
250 self.content_type.and_then(ContentType::parse)
251 }
252
253 #[must_use]
255 pub const fn charset(&self) -> Charset {
256 self.charset
257 }
258
259 #[must_use]
261 pub fn headers(&self) -> Headers<'p> {
262 Headers { rest: self.block }
263 }
264
265 #[must_use]
267 pub const fn header_bytes(&self) -> u32 {
268 self.block.len() as u32
269 }
270}
271
272#[derive(Clone, Copy)]
287pub struct ContentType<'p> {
288 raw: &'p [u8],
289 media: parse_net::MediaType<'p>,
290}
291
292impl fmt::Debug for ContentType<'_> {
293 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
294 f.debug_struct("ContentType")
295 .field("type", &self.type_())
296 .field("subtype", &self.subtype())
297 .finish_non_exhaustive()
298 }
299}
300
301impl<'p> ContentType<'p> {
302 pub(crate) fn parse(raw: &'p [u8]) -> Option<ContentType<'p>> {
303 parse_net::media::parse(raw, &crate::kind::HEADER_LIMITS)
304 .ok()
305 .map(|media| ContentType { raw, media })
306 }
307
308 #[must_use]
310 pub const fn raw(&self) -> &'p [u8] {
311 self.raw
312 }
313
314 #[must_use]
316 pub fn type_(&self) -> &'p str {
317 core::str::from_utf8(self.media.type_()).unwrap_or("")
318 }
319
320 #[must_use]
322 pub fn subtype(&self) -> &'p str {
323 core::str::from_utf8(self.media.subtype()).unwrap_or("")
324 }
325
326 #[must_use]
328 pub fn is(&self, type_: &str, subtype: &str) -> bool {
329 self.media.is(type_, subtype)
330 }
331
332 #[must_use]
335 pub fn parameter(&self, name: &str) -> Option<&'p [u8]> {
336 self.media
337 .params()
338 .get(name)
339 .map(|w| crate::kind::word_raw(&w))
340 }
341
342 #[must_use]
344 pub fn charset(&self) -> Charset {
345 self.parameter("charset")
346 .map_or(Charset::Unspecified, Charset::from_label)
347 }
348}
349
350#[derive(Clone)]
355pub struct Headers<'p> {
356 rest: &'p [u8],
357}
358
359impl fmt::Debug for Headers<'_> {
360 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
361 let mut list = f.debug_list();
362 for (name, _) in self.clone() {
363 list.entry(&core::str::from_utf8(name).unwrap_or("?"));
364 }
365 list.finish()
366 }
367}
368
369impl<'p> Iterator for Headers<'p> {
370 type Item = (&'p [u8], &'p [u8]);
371
372 fn next(&mut self) -> Option<Self::Item> {
373 let lf = self.rest.iter().position(|&b| b == b'\n')?;
374 let line = self.rest.get(..lf.saturating_sub(1)).unwrap_or(&[]);
375 self.rest = self.rest.get(lf.wrapping_add(1)..).unwrap_or(&[]);
376 if line.is_empty() {
377 self.rest = &[];
378 return None;
379 }
380 let colon = line.iter().position(|&b| b == b':').unwrap_or(line.len());
381 let name = line.get(..colon).unwrap_or(&[]);
382 let value = line.get(colon.wrapping_add(1)..).unwrap_or(&[]);
383 Some((name, parse_net::text::trim_ows(value)))
384 }
385}
386
387#[derive(Clone, Copy, Debug, PartialEq, Eq)]
388enum State {
389 Preamble,
390 AfterDelimiter(After),
392 Headers,
393 Body,
394 EndOfPart,
396 Epilogue,
397 Poisoned,
398}
399
400#[derive(Clone, Copy, Debug, PartialEq, Eq)]
401enum After {
402 Start,
403 Dash,
404 Padding(u8),
405 Cr,
406}
407
408#[derive(Clone, Copy, Debug, PartialEq, Eq)]
410enum Token {
411 NotSeen,
412 Capturing,
413 Captured,
414}
415
416const SKIPPING: u8 = 1;
417const IS_TOKEN: u8 = 2;
418const IS_CHARSET: u8 = 4;
419const DEFERRED: u8 = 8;
420
421#[derive(Clone, Copy, Debug)]
423pub(crate) enum Source {
424 Input(usize),
426 Buffer,
428}
429
430#[derive(Clone, Copy, Debug)]
432pub(crate) enum Outcome {
433 PartStart(HeadFacts, Source),
434 DataInput(usize, usize),
436 DataDelimiter(usize),
438 PartEnd(PartSummary),
439 End,
440 NeedInput,
441}
442
443pub struct MultipartParser<B: AsMut<[u8]> + AsRef<[u8]>> {
491 boundary: Boundary,
492 limits: Limits,
493 policy: Policy,
494 buffer: B,
495 state: State,
496 k: u8,
498 bare: u8,
501 flags: u8,
502 token: Token,
503 token_len: u16,
504 charset_len: u8,
505 charset_done: bool,
506 scan: BlockScan,
507 copied: u32,
509 dirty: u32,
514 parts: u32,
515 files: u32,
516 fields: u32,
517 kind: PartKind,
518 part_bytes: u64,
519 text_total: u64,
520 data_start: u64,
521 preamble: u64,
522 epilogue: u64,
523 head_start: u64,
525 epilogue_start: u64,
527 bytes_seen: u64,
528}
529
530impl<B: AsMut<[u8]> + AsRef<[u8]>> fmt::Debug for MultipartParser<B> {
531 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
532 f.debug_struct("MultipartParser")
533 .field("boundary", &self.boundary)
534 .field("state", &self.state)
535 .field("bytes_seen", &self.bytes_seen)
536 .field("parts_seen", &self.parts)
537 .finish_non_exhaustive()
538 }
539}
540
541#[cfg(feature = "alloc")]
542impl MultipartParser<alloc::vec::Vec<u8>> {
543 pub fn new(boundary: Boundary, limits: &Limits, policy: &Policy) -> Result<Self, FormError> {
560 limits.validate_with(policy)?;
561 let buffer = alloc::vec![0u8; limits.multipart_buffer_len(policy)];
562 Self::with_buffer(boundary, limits, policy, buffer)
563 }
564}
565
566impl<B: AsMut<[u8]> + AsRef<[u8]>> MultipartParser<B> {
567 pub fn with_buffer(
586 boundary: Boundary,
587 limits: &Limits,
588 policy: &Policy,
589 buffer: B,
590 ) -> Result<Self, FormError> {
591 limits.validate_with(policy)?;
592 let needed = limits.multipart_buffer_len(policy);
593 if buffer.as_ref().len() < needed {
594 return Err(FormError::BufferTooSmall { needed });
595 }
596 Ok(Self {
597 boundary,
598 limits: *limits,
599 policy: *policy,
600 buffer,
601 state: State::Preamble,
602 k: 2,
605 bare: 1,
606 flags: 0,
607 token: Token::NotSeen,
608 token_len: 0,
609 charset_len: 0,
610 charset_done: false,
611 scan: BlockScan::default(),
612 copied: 0,
613 dirty: 0,
614 parts: 0,
615 files: 0,
616 fields: 0,
617 kind: PartKind::Field,
618 part_bytes: 0,
619 text_total: 0,
620 data_start: 0,
621 preamble: 0,
622 epilogue: 0,
623 head_start: 0,
624 epilogue_start: 0,
625 bytes_seen: 0,
626 })
627 }
628
629 pub fn parse<'p>(
637 &'p mut self,
638 input: &'p [u8],
639 eof: bool,
640 ) -> Result<MultipartStep<'p>, FormError> {
641 let (consumed, outcome) = self.advance(input, eof)?;
642 Ok(MultipartStep {
643 consumed,
644 event: self.view(outcome, input),
645 })
646 }
647
648 pub fn skip_part(&mut self) {
652 if matches!(self.state, State::Body) {
653 self.flags |= SKIPPING;
654 }
655 }
656
657 pub fn reset(&mut self, boundary: Boundary) {
660 self.wipe_used();
661 let limits = self.limits;
662 let policy = self.policy;
663 self.boundary = boundary;
664 self.state = State::Preamble;
665 self.k = 2;
666 self.bare = 1;
667 self.flags = 0;
668 self.token = Token::NotSeen;
669 self.token_len = 0;
670 self.charset_len = 0;
671 self.charset_done = false;
672 self.scan = BlockScan::default();
673 self.copied = 0;
674 self.parts = 0;
675 self.files = 0;
676 self.fields = 0;
677 self.kind = PartKind::Field;
678 self.part_bytes = 0;
679 self.text_total = 0;
680 self.data_start = 0;
681 self.preamble = 0;
682 self.epilogue = 0;
683 self.head_start = 0;
684 self.epilogue_start = 0;
685 self.bytes_seen = 0;
686 self.limits = limits;
687 self.policy = policy;
688 }
689
690 #[must_use]
692 pub const fn bytes_seen(&self) -> u64 {
693 self.bytes_seen
694 }
695
696 #[must_use]
698 pub const fn parts_seen(&self) -> u32 {
699 self.parts
700 }
701
702 #[must_use]
704 pub const fn files_seen(&self) -> u32 {
705 self.files
706 }
707
708 #[must_use]
710 pub fn is_done(&self) -> bool {
711 self.state == State::Epilogue
712 }
713
714 #[must_use]
716 pub const fn limits(&self) -> &Limits {
717 &self.limits
718 }
719
720 #[must_use]
722 pub const fn policy(&self) -> &Policy {
723 &self.policy
724 }
725
726 #[must_use]
728 pub fn captured_token(&self) -> Option<&str> {
729 if self.token != Token::Captured {
730 return None;
731 }
732 let start = self.token_area();
733 let bytes = self
734 .buffer
735 .as_ref()
736 .get(start..start.saturating_add(usize::from(self.token_len)))?;
737 core::str::from_utf8(bytes).ok()
738 }
739
740 #[must_use]
742 pub fn declared_charset(&self) -> Option<&[u8]> {
743 if !self.charset_done {
744 return None;
745 }
746 let start = self.charset_area();
747 self.buffer
748 .as_ref()
749 .get(start..start.saturating_add(usize::from(self.charset_len)))
750 }
751
752 #[cfg(feature = "alloc")]
754 pub(crate) fn area_name(&self, len: u32) -> &str {
755 let start = self.name_area();
756 let bytes = self
757 .buffer
758 .as_ref()
759 .get(start..start.saturating_add(len as usize))
760 .unwrap_or(&[]);
761 core::str::from_utf8(bytes).unwrap_or("")
762 }
763
764 #[cfg(feature = "alloc")]
766 pub(crate) fn area_filename(&self, len: u32) -> &[u8] {
767 let start = self.filename_area();
768 self.buffer
769 .as_ref()
770 .get(start..start.saturating_add(len as usize))
771 .unwrap_or(&[])
772 }
773
774 #[cfg(feature = "alloc")]
776 pub(crate) const fn data_start(&self) -> u64 {
777 self.data_start
778 }
779
780 fn name_area(&self) -> usize {
783 self.limits.max_part_header_bytes as usize
784 }
785
786 fn filename_area(&self) -> usize {
787 self.name_area().saturating_add(self.limits.name() as usize)
788 }
789
790 fn token_area(&self) -> usize {
791 let filename = if matches!(
792 self.policy.extended_filename,
793 crate::ExtendedFilenamePolicy::AcceptAlone
794 ) {
795 self.limits.filename() as usize
796 } else {
797 0
798 };
799 self.filename_area().saturating_add(filename)
800 }
801
802 fn charset_area(&self) -> usize {
803 let token = if self.policy.token_field.is_some() {
804 usize::from(self.limits.token())
805 } else {
806 0
807 };
808 self.token_area().saturating_add(token)
809 }
810
811 fn head_at(&self) -> Location {
813 Location::in_part(self.parts.checked_sub(1), self.head_start)
814 }
815
816 fn excess(&self, used: u64, limit: u64) -> Location {
819 let into_part = self.part_bytes.saturating_add(limit.saturating_sub(used));
820 Location::in_part(
821 self.parts.checked_sub(1),
822 self.data_start.saturating_add(into_part),
823 )
824 }
825
826 fn at(&self, pos: usize) -> Location {
827 self.at_offset(self.bytes_seen.saturating_add(pos as u64))
828 }
829
830 fn at_offset(&self, offset: u64) -> Location {
832 let part = if matches!(self.state, State::Preamble | State::Epilogue) {
833 None
834 } else {
835 self.parts.checked_sub(1)
836 };
837 Location::in_part(part, offset)
838 }
839
840 pub(crate) fn advance(
844 &mut self,
845 input: &[u8],
846 eof: bool,
847 ) -> Result<(usize, Outcome), FormError> {
848 if self.state == State::Poisoned {
849 return Err(FormError::Poisoned);
850 }
851 match self.run(input, eof) {
852 Ok((consumed, outcome)) => {
853 self.bytes_seen = self.bytes_seen.saturating_add(consumed as u64);
854 Ok((consumed, outcome))
855 }
856 Err(e) => {
857 self.state = State::Poisoned;
858 Err(e)
859 }
860 }
861 }
862
863 #[cold]
868 #[inline(never)]
869 fn run_over_limit(&mut self, input: &[u8]) -> Result<(usize, Outcome), FormError> {
870 let over = FormError::limit(Limit::Body, Location::at(self.limits.body()));
871 let room = self.limits.body().saturating_sub(self.bytes_seen);
872 if room == 0 {
873 return Err(over);
874 }
875 let within = input.get(..room as usize).unwrap_or(&[]);
876 let (consumed, outcome) = self.run(within, false)?;
877 if matches!(outcome, Outcome::NeedInput | Outcome::End) {
880 return Err(over);
881 }
882 Ok((consumed, outcome))
883 }
884
885 fn run(&mut self, input: &[u8], eof: bool) -> Result<(usize, Outcome), FormError> {
886 if self.bytes_seen.saturating_add(input.len() as u64) > self.limits.body() {
888 return self.run_over_limit(input);
889 }
890 let mut pos = 0usize;
891 loop {
892 let hay = input.get(pos..).unwrap_or(&[]);
893 match self.state {
894 State::Poisoned => return Err(FormError::Poisoned),
895 State::Preamble => {
896 if let Some(after) = self.preamble_step(hay)? {
897 pos = pos.wrapping_add(after);
898 self.state = State::AfterDelimiter(After::Start);
899 continue;
900 }
901 if eof {
902 return Err(FormError::syntax(
903 SyntaxError::NoInitialDelimiter,
904 self.at(input.len()),
905 ));
906 }
907 return Ok((input.len(), Outcome::NeedInput));
908 }
909 State::AfterDelimiter(sub) => {
910 let Some(&c) = hay.first() else {
911 return self.need_input(input.len(), eof);
912 };
913 pos = pos.wrapping_add(1);
914 let at = self.at(pos);
915 self.state = match (sub, c) {
916 (After::Start, b'-') => State::AfterDelimiter(After::Dash),
917 (After::Dash, b'-') => {
918 self.state = State::Epilogue;
919 self.epilogue_start = self.bytes_seen.saturating_add(pos as u64);
920 return Ok((pos, Outcome::End));
921 }
922 (After::Start, b' ' | b'\t') => {
923 if self.limits.max_padding_bytes == 0 {
924 return Err(FormError::limit(Limit::Padding, at));
925 }
926 State::AfterDelimiter(After::Padding(1))
927 }
928 (After::Padding(n), b' ' | b'\t') => {
929 if n >= self.limits.max_padding_bytes {
930 return Err(FormError::limit(Limit::Padding, at));
931 }
932 State::AfterDelimiter(After::Padding(n.saturating_add(1)))
933 }
934 (After::Start | After::Padding(_), b'\r') => {
935 State::AfterDelimiter(After::Cr)
936 }
937 (After::Cr, b'\n') => {
938 self.parts = self.parts.saturating_add(1);
939 if self.parts > self.limits.max_parts {
940 return Err(FormError::limit(Limit::Parts, self.at(pos)));
941 }
942 self.scan = BlockScan::default();
943 self.copied = 0;
944 self.head_start = self.bytes_seen.saturating_add(pos as u64);
945 State::Headers
946 }
947 (After::Start | After::Padding(_), b'\n') => {
948 return Err(FormError::syntax(SyntaxError::BareLineFeed, at));
949 }
950 _ => return Err(FormError::syntax(SyntaxError::BoundaryInContent, at)),
951 };
952 }
953 State::Headers => {
954 let at = self.head_at();
955 match self.scan.feed(hay, &self.limits, at)? {
956 Some(end) => {
957 let source = if self.copied == 0 {
958 Source::Input(pos)
959 } else {
960 self.copy_header(hay.get(..end).unwrap_or(&[]), at)?;
961 Source::Buffer
962 };
963 let block_start_in_input = pos;
964 pos = pos.wrapping_add(end);
965 let facts =
966 self.read_head(input, source, block_start_in_input, end, at)?;
967 let (is_token, is_charset) = {
968 let block = self.block(&facts, source, input);
969 let name = self.name_bytes(&facts, block);
970 let token = self
971 .policy
972 .token_field
973 .is_some_and(|t| name == t.name().as_bytes());
974 (token, name == b"_charset_")
975 };
976 self.start_part(&facts, is_token, is_charset, at)?;
977 self.data_start = self.bytes_seen.saturating_add(pos as u64);
978 return Ok((pos, Outcome::PartStart(facts, source)));
979 }
980 None => {
981 self.copy_header(hay, at)?;
982 return self.need_input(input.len(), eof);
983 }
984 }
985 }
986 State::Body => {
987 if hay.is_empty() {
988 return self.need_input(input.len(), eof);
989 }
990 match self.body_step(hay, pos, input.len(), eof)? {
991 BodyStep::Emit(consumed, outcome) => return Ok((consumed, outcome)),
992 BodyStep::Continue(next) => pos = next,
993 }
994 }
995 State::EndOfPart => {
996 let summary = self.end_part(self.at(pos))?;
997 self.state = State::AfterDelimiter(After::Start);
998 return Ok((pos, Outcome::PartEnd(summary)));
999 }
1000 State::Epilogue => {
1001 self.epilogue = self.epilogue.saturating_add(hay.len() as u64);
1002 if self.epilogue > self.limits.epilogue() {
1003 let at = self.epilogue_start.saturating_add(self.limits.epilogue());
1004 return Err(FormError::limit(Limit::Epilogue, Location::at(at)));
1005 }
1006 return Ok((input.len(), Outcome::End));
1007 }
1008 }
1009 }
1010 }
1011
1012 fn need_input(&self, len: usize, eof: bool) -> Result<(usize, Outcome), FormError> {
1013 if eof {
1014 Err(FormError::syntax(SyntaxError::Truncated, self.at(len)))
1015 } else {
1016 Ok((len, Outcome::NeedInput))
1017 }
1018 }
1019
1020 fn bare_break(&self, after: usize, pos: usize) -> FormError {
1024 let end = self
1025 .bytes_seen
1026 .saturating_add(pos.wrapping_add(after) as u64);
1027 let bare = self.boundary.len().saturating_sub(1) as u64;
1028 FormError::syntax(
1029 SyntaxError::BoundaryInContent,
1030 self.at_offset(end.saturating_sub(bare)),
1031 )
1032 }
1033
1034 fn preamble_step(&mut self, hay: &[u8]) -> Result<Option<usize>, FormError> {
1037 let len = self.boundary.len() as u64;
1038 let budget = self.limits.preamble().saturating_add(len);
1040 let room = usize::try_from(budget.saturating_sub(self.preamble)).unwrap_or(usize::MAX);
1041 let window = hay.get(..room.min(hay.len())).unwrap_or(hay);
1042 let (k, m, found) = self
1043 .boundary
1044 .run(usize::from(self.k), usize::from(self.bare), window);
1045 self.k = k as u8;
1046 self.bare = m as u8;
1047 match found {
1048 Hit::BareBreak(after) => Err(self.bare_break(after, 0)),
1049 Hit::Delimiter(after) => {
1050 let end = self.preamble.saturating_add(after as u64);
1053 let preamble = end.saturating_sub(len);
1054 if preamble > self.limits.preamble() {
1055 return Err(FormError::limit(
1056 Limit::Preamble,
1057 Location::at(self.limits.preamble()),
1058 ));
1059 }
1060 self.k = 0;
1061 self.bare = 0;
1062 Ok(Some(after))
1063 }
1064 Hit::None => {
1065 self.preamble = self.preamble.saturating_add(window.len() as u64);
1066 if window.len() < hay.len() || self.preamble >= budget {
1067 return Err(FormError::limit(
1068 Limit::Preamble,
1069 Location::at(self.limits.preamble()),
1070 ));
1071 }
1072 Ok(None)
1073 }
1074 }
1075 }
1076
1077 fn copy_header(&mut self, bytes: &[u8], at: Location) -> Result<(), FormError> {
1078 let start = self.copied as usize;
1079 let end = start.saturating_add(bytes.len());
1080 if end > self.limits.max_part_header_bytes as usize {
1081 return Err(FormError::limit(Limit::PartHeaderBytes, at));
1082 }
1083 match self.buffer.as_mut().get_mut(start..end) {
1084 Some(dst) => dst.copy_from_slice(bytes),
1085 None => return Err(FormError::limit(Limit::PartHeaderBytes, at)),
1086 }
1087 self.copied = end as u32;
1088 self.dirty = self.dirty.max(self.copied);
1089 Ok(())
1090 }
1091
1092 fn read_head(
1093 &mut self,
1094 input: &[u8],
1095 source: Source,
1096 start: usize,
1097 end: usize,
1098 at: Location,
1099 ) -> Result<HeadFacts, FormError> {
1100 let name_area = self.name_area();
1101 let filename_area = self.filename_area();
1102 let filename_end = self.token_area();
1103 let limits = self.limits;
1104 let policy = self.policy;
1105 let copied = self.copied as usize;
1106 let block_len = match source {
1107 Source::Input(_) => end,
1108 Source::Buffer => copied,
1109 };
1110 self.dirty = self.dirty.max(u32::try_from(block_len).unwrap_or(u32::MAX));
1111 let buf = self.buffer.as_mut();
1112 let (head, rest) = buf.split_at_mut(name_area.min(buf.len()));
1113 let (name, rest) =
1114 rest.split_at_mut(filename_area.saturating_sub(name_area).min(rest.len()));
1115 let filename = rest
1116 .get_mut(..filename_end.saturating_sub(filename_area))
1117 .unwrap_or(&mut []);
1118 let block = match source {
1119 Source::Input(_) => input.get(start..start.saturating_add(end)).unwrap_or(&[]),
1120 Source::Buffer => head.get(..copied).unwrap_or(&[]),
1121 };
1122 read_block(block, &limits, &policy, Areas { name, filename }, at)
1123 }
1124
1125 fn start_part(
1127 &mut self,
1128 facts: &HeadFacts,
1129 is_token: bool,
1130 is_charset: bool,
1131 at: Location,
1132 ) -> Result<(), FormError> {
1133 self.state = State::Body;
1134 self.k = 0;
1135 self.bare = 1;
1138 self.flags = 0;
1139 self.kind = facts.kind;
1140 self.part_bytes = 0;
1141 match facts.kind {
1142 PartKind::Field => {
1143 self.fields = self.fields.saturating_add(1);
1144 if self.fields > self.limits.max_fields {
1145 return Err(FormError::limit(Limit::Fields, at));
1146 }
1147 if is_token {
1148 if self.token != Token::NotSeen {
1149 return Err(FormError::policy(PolicyRefusal::DuplicateToken, at));
1150 }
1151 self.token = Token::Capturing;
1152 self.token_len = 0;
1153 self.flags |= IS_TOKEN;
1154 }
1155 if is_charset {
1156 self.flags |= IS_CHARSET;
1157 self.charset_len = 0;
1158 self.charset_done = false;
1159 }
1160 }
1161 PartKind::File => {
1162 let empty = matches!(facts.filename, Some((Text::Block(span), _)) if span.len == 0)
1163 || matches!(facts.filename, Some((Text::Area(0), _)));
1164 if empty && self.policy.empty_files == EmptyFilePolicy::Skip {
1165 self.flags |= DEFERRED;
1166 } else {
1167 self.count_file(at)?;
1168 }
1169 }
1170 }
1171 Ok(())
1172 }
1173
1174 fn block<'a>(&'a self, facts: &HeadFacts, source: Source, input: &'a [u8]) -> &'a [u8] {
1176 let len = facts.block_len as usize;
1177 match source {
1178 Source::Input(start) => input.get(start..start.saturating_add(len)).unwrap_or(&[]),
1179 Source::Buffer => self.buffer.as_ref().get(..len).unwrap_or(&[]),
1180 }
1181 }
1182
1183 fn name_bytes<'a>(&'a self, facts: &HeadFacts, block: &'a [u8]) -> &'a [u8] {
1185 match facts.name {
1186 Text::Block(span) => span.get(block),
1187 Text::Area(len) => {
1188 let start = self.name_area();
1189 self.buffer
1190 .as_ref()
1191 .get(start..start.saturating_add(len as usize))
1192 .unwrap_or(&[])
1193 }
1194 }
1195 }
1196
1197 fn head_view<'p>(&'p self, facts: &HeadFacts, source: Source, input: &'p [u8]) -> PartHead<'p> {
1199 let block = self.block(facts, source, input);
1200 let name = core::str::from_utf8(self.name_bytes(facts, block)).unwrap_or("");
1201 let filename = facts.filename.map(|(text, origin)| {
1202 let raw = match text {
1203 Text::Block(span) => span.get(block),
1204 Text::Area(len) => {
1205 let start = self.filename_area();
1206 self.buffer
1207 .as_ref()
1208 .get(start..start.saturating_add(len as usize))
1209 .unwrap_or(&[])
1210 }
1211 };
1212 FileName::new(raw, origin)
1213 });
1214 PartHead {
1215 index: self.parts.saturating_sub(1),
1216 block,
1217 name,
1218 name_raw: facts.name_raw.get(block),
1219 kind: facts.kind,
1220 filename,
1221 content_type: facts.content_type.map(|span| span.get(block)),
1222 charset: facts.charset,
1223 }
1224 }
1225
1226 fn view<'p>(&'p self, outcome: Outcome, input: &'p [u8]) -> MultipartEvent<'p> {
1228 match outcome {
1229 Outcome::PartStart(facts, source) => {
1230 MultipartEvent::PartStart(self.head_view(&facts, source, input))
1231 }
1232 Outcome::DataInput(start, end) => {
1233 MultipartEvent::Data(input.get(start..end).unwrap_or(&[]))
1234 }
1235 Outcome::DataDelimiter(n) => {
1236 MultipartEvent::Data(self.boundary.delimiter().get(..n).unwrap_or(&[]))
1237 }
1238 Outcome::PartEnd(summary) => MultipartEvent::PartEnd(summary),
1239 Outcome::End => MultipartEvent::End,
1240 Outcome::NeedInput => MultipartEvent::NeedInput,
1241 }
1242 }
1243
1244 fn count_file(&mut self, at: Location) -> Result<(), FormError> {
1245 self.files = self.files.saturating_add(1);
1246 if self.files > self.limits.max_files {
1247 return Err(FormError::limit(Limit::Files, at));
1248 }
1249 if let Some(t) = self.policy.token_field {
1250 if t.placement() == TokenPlacement::BeforeFirstFile && self.token != Token::Captured {
1251 return Err(FormError::policy(PolicyRefusal::TokenFieldLate, at));
1252 }
1253 }
1254 Ok(())
1255 }
1256
1257 fn take_data(&mut self, bytes: &[u8]) -> Result<(), FormError> {
1260 let n = bytes.len() as u64;
1261 if n == 0 {
1262 return Ok(());
1263 }
1264 if self.flags & DEFERRED != 0 {
1265 self.flags &= !DEFERRED;
1266 let at = Location::in_part(self.parts.checked_sub(1), self.data_start);
1267 self.count_file(at)?;
1268 }
1269 let total = self.part_bytes.saturating_add(n);
1270 match self.kind {
1271 PartKind::File => {
1272 let limit = self.limits.file();
1273 if total > limit {
1274 let at = self.excess(self.part_bytes, limit);
1275 return Err(FormError::limit(Limit::FileSize, at));
1276 }
1277 }
1278 PartKind::Field => {
1279 let limit = u64::from(self.limits.value());
1280 if total > limit {
1281 let at = self.excess(self.part_bytes, limit);
1282 return Err(FormError::limit(Limit::ValueLength, at));
1283 }
1284 let text = self.text_total.saturating_add(n);
1285 let limit = self.limits.text();
1286 if text > limit {
1287 let at = self.excess(self.text_total, limit);
1288 return Err(FormError::limit(Limit::TextTotal, at));
1289 }
1290 self.text_total = text;
1291 }
1292 }
1293 if self.flags & IS_TOKEN != 0 {
1294 let start = self
1295 .token_area()
1296 .saturating_add(usize::from(self.token_len));
1297 let end = start.saturating_add(bytes.len());
1298 let limit = u64::from(self.limits.token());
1299 let at = self.excess(u64::from(self.token_len), limit);
1300 if end
1301 > self
1302 .token_area()
1303 .saturating_add(usize::from(self.limits.token()))
1304 {
1305 return Err(FormError::limit(Limit::TokenLength, at));
1306 }
1307 match self.buffer.as_mut().get_mut(start..end) {
1308 Some(dst) => dst.copy_from_slice(bytes),
1309 None => return Err(FormError::limit(Limit::TokenLength, at)),
1310 }
1311 self.token_len = self.token_len.saturating_add(bytes.len() as u16);
1312 }
1313 if self.flags & IS_CHARSET != 0 {
1314 let start = self
1315 .charset_area()
1316 .saturating_add(usize::from(self.charset_len));
1317 let end = start.saturating_add(bytes.len());
1318 let at = self.excess(u64::from(self.charset_len), CHARSET_CAPTURE as u64);
1319 if end > self.charset_area().saturating_add(CHARSET_CAPTURE) {
1320 return Err(FormError::encoding(EncodingError::UnsupportedCharset, at));
1321 }
1322 match self.buffer.as_mut().get_mut(start..end) {
1323 Some(dst) => dst.copy_from_slice(bytes),
1324 None => return Err(FormError::encoding(EncodingError::UnsupportedCharset, at)),
1325 }
1326 self.charset_len = self.charset_len.saturating_add(bytes.len() as u8);
1327 }
1328 self.part_bytes = total;
1329 Ok(())
1330 }
1331
1332 fn end_part(&mut self, at: Location) -> Result<PartSummary, FormError> {
1333 if self.flags & IS_TOKEN != 0 {
1334 let start = self.token_area();
1335 let bytes = self
1336 .buffer
1337 .as_ref()
1338 .get(start..start.saturating_add(usize::from(self.token_len)))
1339 .unwrap_or(&[]);
1340 if core::str::from_utf8(bytes).is_err() {
1341 return Err(FormError::encoding(EncodingError::InvalidUtf8, at));
1342 }
1343 self.token = Token::Captured;
1344 }
1345 if self.flags & IS_CHARSET != 0 {
1346 self.charset_done = true;
1347 }
1348 let summary = PartSummary {
1349 index: self.parts.saturating_sub(1),
1350 kind: self.kind,
1351 bytes: self.part_bytes,
1352 skipped: self.flags & SKIPPING != 0,
1353 };
1354 self.flags = 0;
1355 self.k = 0;
1356 Ok(summary)
1357 }
1358
1359 fn body_step(
1361 &mut self,
1362 hay: &[u8],
1363 pos: usize,
1364 input_len: usize,
1365 eof: bool,
1366 ) -> Result<BodyStep, FormError> {
1367 let len = self.boundary.len();
1368 let k0 = usize::from(self.k);
1369 if k0 > 0 {
1370 let need = self.boundary.delimiter().get(k0..).unwrap_or(&[]);
1374 let (need_len, n) = (need.len(), super::boundary::shared(need, hay));
1375 if n == need_len {
1376 self.k = 0;
1378 self.bare = 0;
1379 self.state = State::EndOfPart;
1380 return self.emit_delimiter_bytes(0, pos.wrapping_add(n));
1381 }
1382 if n < hay.len() {
1383 self.k = 0;
1387 self.bare = k0.max(2).wrapping_sub(1) as u8;
1388 return self.emit_delimiter_bytes(k0, pos);
1389 }
1390 if eof {
1392 let boundary = self.boundary;
1395 self.take_data(boundary.delimiter().get(..k0).unwrap_or(&[]))?;
1396 self.take_data(hay)?;
1397 return Err(FormError::syntax(
1398 SyntaxError::Truncated,
1399 self.at(input_len),
1400 ));
1401 }
1402 self.k = k0.saturating_add(n) as u8;
1403 return self.emit_delimiter_bytes(0, input_len);
1404 }
1405 let (k, m, found) = self.boundary.run(0, usize::from(self.bare), hay);
1406 match found {
1407 Hit::BareBreak(after) => {
1408 let start = after.saturating_sub(len.saturating_sub(1));
1411 self.take_data(hay.get(..start).unwrap_or(&[]))?;
1412 Err(self.bare_break(after, pos))
1413 }
1414 Hit::Delimiter(after) => {
1415 let data = after.saturating_sub(len);
1416 self.k = 0;
1417 self.bare = 0;
1418 self.state = State::EndOfPart;
1419 let bytes = hay.get(..data).unwrap_or(&[]);
1420 self.emit_input_bytes(bytes, pos, pos.wrapping_add(data), pos.wrapping_add(after))
1421 }
1422 Hit::None => {
1423 if eof {
1424 self.take_data(hay)?;
1426 return Err(FormError::syntax(
1427 SyntaxError::Truncated,
1428 self.at(input_len),
1429 ));
1430 }
1431 self.k = k as u8;
1432 self.bare = m as u8;
1433 let data_end = hay.len().saturating_sub(k);
1434 let bytes = hay.get(..data_end).unwrap_or(&[]);
1435 self.emit_input_bytes(bytes, pos, pos.wrapping_add(data_end), input_len)
1436 }
1437 }
1438 }
1439
1440 fn emit_delimiter_bytes(&mut self, n: usize, consumed: usize) -> Result<BodyStep, FormError> {
1443 let boundary = self.boundary;
1444 let bytes = boundary.delimiter().get(..n).unwrap_or(&[]);
1445 self.take_data(bytes)?;
1446 if n == 0 || self.flags & SKIPPING != 0 {
1447 return Ok(BodyStep::Continue(consumed));
1448 }
1449 Ok(BodyStep::Emit(consumed, Outcome::DataDelimiter(n)))
1450 }
1451
1452 fn emit_input_bytes(
1455 &mut self,
1456 bytes: &[u8],
1457 start: usize,
1458 end: usize,
1459 consumed: usize,
1460 ) -> Result<BodyStep, FormError> {
1461 self.take_data(bytes)?;
1462 if start == end || self.flags & SKIPPING != 0 {
1463 return Ok(BodyStep::Continue(consumed));
1464 }
1465 Ok(BodyStep::Emit(consumed, Outcome::DataInput(start, end)))
1466 }
1467}
1468
1469enum BodyStep {
1471 Emit(usize, Outcome),
1472 Continue(usize),
1473}
1474
1475impl<B: AsMut<[u8]> + AsRef<[u8]>> MultipartParser<B> {
1476 fn wipe_used(&mut self) {
1480 let used = self.dirty as usize;
1481 let regions = [
1482 (0, used.min(self.name_area())),
1483 (
1484 self.name_area(),
1485 used.min(self.filename_area().saturating_sub(self.name_area())),
1486 ),
1487 (
1488 self.filename_area(),
1489 used.min(self.token_area().saturating_sub(self.filename_area())),
1490 ),
1491 (self.token_area(), usize::from(self.token_len)),
1492 (self.charset_area(), CHARSET_CAPTURE),
1495 ];
1496 let buf = self.buffer.as_mut();
1497 for (start, len) in regions {
1498 if let Some(region) = buf.get_mut(start..start.saturating_add(len)) {
1499 crate::wipe::wipe(region);
1500 }
1501 }
1502 self.dirty = 0;
1503 }
1504}
1505
1506impl<B: AsMut<[u8]> + AsRef<[u8]>> Drop for MultipartParser<B> {
1507 fn drop(&mut self) {
1508 self.wipe_used();
1509 }
1510}
1511
1512#[cfg(test)]
1513mod tests {
1514 use super::*;
1515
1516 #[test]
1521 fn test_wipe_covers_every_area_written() {
1522 let policy = Policy::DEFAULT
1523 .with_extended_filename(crate::ExtendedFilenamePolicy::AcceptAlone)
1524 .with_token_field(crate::TokenField::new("csrf"));
1525 let limits = Limits::DEFAULT.with_max_part_header_bytes(512);
1526 let long = "x".repeat(300);
1527 let body = alloc::format!(
1528 "--X\r\nContent-Disposition: form-data; name=csrf\r\n\r\nsecret-token-value\r\n\
1529 --X\r\nContent-Disposition: form-data; name=_charset_\r\n\r\nUTF-8\r\n\
1530 --X\r\nContent-Disposition: form-data; name=\"pass%22word\"\r\nX-Pad: {long}\r\n\r\nhunter2\r\n\
1531 --X\r\nContent-Disposition: form-data; name=f; filename*=UTF-8''caf%C3%A9.txt\r\n\r\nfile\r\n--X--"
1532 );
1533 let body = body.replace(
1536 "name=_charset_\r\n\r\nUTF-8\r\n",
1537 "name=_charset_\r\n\r\nunicode-1-1-utf-8\r\n--X\r\nContent-Disposition: form-data; name=_charset_\r\n\r\nutf8\r\n",
1538 );
1539 assert!(body.contains("unicode-1-1-utf-8"));
1540 for split in [usize::MAX, 7, 1] {
1541 let mut buf = alloc::vec![0u8; limits.multipart_buffer_len(&policy)];
1542 {
1543 let mut p = MultipartParser::with_buffer(
1544 Boundary::new(b"X").unwrap(),
1545 &limits,
1546 &policy,
1547 &mut buf[..],
1548 )
1549 .unwrap();
1550 let bytes = body.as_bytes();
1551 let chunks: alloc::vec::Vec<&[u8]> = bytes.chunks(split.min(bytes.len())).collect();
1552 let mut ended = false;
1553 for (i, c) in chunks.iter().enumerate() {
1554 let last = i + 1 == chunks.len();
1555 let mut rest = *c;
1556 loop {
1557 let step = p.parse(rest, last).unwrap();
1558 rest = &rest[step.consumed..];
1559 match step.event {
1560 MultipartEvent::NeedInput => break,
1561 MultipartEvent::End => {
1562 ended = true;
1563 break;
1564 }
1565 _ => {}
1566 }
1567 }
1568 }
1569 assert!(ended);
1570 assert_eq!(p.captured_token(), Some("secret-token-value"));
1571 }
1572 assert!(
1573 buf.iter().all(|&b| b == 0),
1574 "split {split}: bytes left at {:?}",
1575 buf.iter().position(|&b| b != 0)
1576 );
1577 }
1578 }
1579
1580 #[cfg(feature = "alloc")]
1582 fn run(p: &mut MultipartParser<alloc::vec::Vec<u8>>, body: &[u8]) -> Option<FormError> {
1583 let mut rest = body;
1584 loop {
1585 match p.parse(rest, true) {
1586 Err(e) => return Some(e),
1587 Ok(step) => {
1588 rest = &rest[step.consumed..];
1589 if matches!(step.event, MultipartEvent::End) {
1590 return None;
1591 }
1592 }
1593 }
1594 }
1595 }
1596
1597 #[cfg(feature = "alloc")]
1601 #[test]
1602 fn test_attack_t28_counters_near_u64_max_never_wrap() {
1603 let body = b"--X\r\nContent-Disposition: form-data; name=a\r\n\r\nvalue\r\n--X--";
1604 let new = |limits: &Limits| {
1605 MultipartParser::new(Boundary::new(b"X").unwrap(), limits, &Policy::DEFAULT).unwrap()
1606 };
1607 let mut p = new(&Limits::DEFAULT);
1608 p.bytes_seen = u64::MAX - 4;
1609 assert!(matches!(
1610 run(&mut p, body),
1611 Some(FormError::Limit {
1612 limit: crate::error::Limit::Body,
1613 ..
1614 })
1615 ));
1616
1617 let mut p = new(&Limits::DEFAULT);
1618 p.text_total = u64::MAX - 2;
1619 assert!(matches!(
1620 run(&mut p, body),
1621 Some(FormError::Limit {
1622 limit: crate::error::Limit::TextTotal,
1623 ..
1624 })
1625 ));
1626
1627 let open = Limits::DEFAULT
1628 .with_max_body_bytes(u64::MAX)
1629 .with_max_text_bytes(u64::MAX);
1630 let mut p = new(&open);
1631 p.bytes_seen = u64::MAX - 4;
1632 assert!(run(&mut p, body).is_none());
1633 assert_eq!(p.bytes_seen, u64::MAX);
1634 }
1635
1636 #[test]
1637 fn test_parser_size_is_bounded() {
1638 assert!(core::mem::size_of::<MultipartParser<&mut [u8]>>() <= 384);
1641 assert!(core::mem::size_of::<Boundary>() <= 150);
1642 }
1643
1644 #[cfg(feature = "alloc")]
1645 #[test]
1646 fn test_debug_never_shows_data_or_file_names() {
1647 let body = b"--X\r\nContent-Disposition: form-data; name=f; filename=\"secret.pdf\"\r\n\r\nhunter2\r\n--X--";
1648 let mut p = MultipartParser::new(
1649 Boundary::new(b"X").unwrap(),
1650 &Limits::DEFAULT,
1651 &Policy::DEFAULT,
1652 )
1653 .unwrap();
1654 let mut rest: &[u8] = body;
1655 let mut seen = alloc::string::String::new();
1656 loop {
1657 let step = p.parse(rest, true).unwrap();
1658 rest = &rest[step.consumed..];
1659 if let MultipartEvent::PartStart(head) = &step.event {
1660 seen.push_str(&alloc::format!("{:?}", head.headers()));
1661 }
1662 seen.push_str(&alloc::format!("{step:?}"));
1663 if matches!(step.event, MultipartEvent::End) {
1664 break;
1665 }
1666 }
1667 assert!(seen.contains("Data(7 bytes)"), "{seen}");
1668 assert!(
1669 !seen.contains("hunter2") && !seen.contains("secret"),
1670 "{seen}"
1671 );
1672 }
1673
1674 #[test]
1675 fn test_charset_labels() {
1676 assert_eq!(Charset::from_label(b"UTF-8"), Charset::Utf8);
1677 assert_eq!(Charset::from_label(b"us-ascii"), Charset::Ascii);
1678 assert_eq!(Charset::from_label(b"utf8"), Charset::Other);
1679 }
1680}