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
61impl Charset {
62 pub(crate) fn from_label(label: &[u8]) -> Charset {
63 if label.eq_ignore_ascii_case(b"utf-8") {
64 Charset::Utf8
65 } else if label.eq_ignore_ascii_case(b"us-ascii") {
66 Charset::Ascii
67 } else {
68 Charset::Other
69 }
70 }
71}
72
73#[non_exhaustive]
76#[derive(Debug)]
77pub struct Step<'p> {
78 pub consumed: usize,
81 pub event: Event<'p>,
83}
84
85#[non_exhaustive]
93pub enum Event<'p> {
94 PartStart(PartHead<'p>),
96 Data(&'p [u8]),
98 PartEnd(PartSummary),
100 End,
102 NeedInput,
104}
105
106impl fmt::Debug for Event<'_> {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 match self {
109 Self::PartStart(head) => f.debug_tuple("PartStart").field(head).finish(),
110 Self::Data(bytes) => write!(f, "Data({} bytes)", bytes.len()),
111 Self::PartEnd(summary) => f.debug_tuple("PartEnd").field(summary).finish(),
112 Self::End => f.write_str("End"),
113 Self::NeedInput => f.write_str("NeedInput"),
114 }
115 }
116}
117
118#[non_exhaustive]
120#[derive(Clone, Copy, Debug, PartialEq, Eq)]
121pub struct PartSummary {
122 pub index: u32,
124 pub kind: PartKind,
126 pub bytes: u64,
128 pub skipped: bool,
130}
131
132#[derive(Clone, Copy)]
136pub struct PartHead<'p> {
137 index: u32,
138 block: &'p [u8],
139 name: &'p str,
140 name_raw: &'p [u8],
141 kind: PartKind,
142 filename: Option<FileName<'p>>,
143 content_type: Option<&'p [u8]>,
144 charset: Charset,
145}
146
147impl fmt::Debug for PartHead<'_> {
148 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149 f.debug_struct("PartHead")
150 .field("index", &self.index)
151 .field("name", &self.name)
152 .field("kind", &self.kind)
153 .field("filename", &self.filename)
154 .finish_non_exhaustive()
155 }
156}
157
158impl<'p> PartHead<'p> {
159 #[cfg(feature = "alloc")]
161 #[allow(clippy::too_many_arguments)]
162 pub(crate) const fn from_parts(
163 index: u32,
164 block: &'p [u8],
165 name: &'p str,
166 name_raw: &'p [u8],
167 kind: PartKind,
168 filename: Option<FileName<'p>>,
169 content_type: Option<&'p [u8]>,
170 charset: Charset,
171 ) -> Self {
172 Self {
173 index,
174 block,
175 name,
176 name_raw,
177 kind,
178 filename,
179 content_type,
180 charset,
181 }
182 }
183
184 #[cfg(feature = "alloc")]
186 pub(crate) const fn block(&self) -> &'p [u8] {
187 self.block
188 }
189
190 #[must_use]
192 pub const fn index(&self) -> u32 {
193 self.index
194 }
195
196 #[must_use]
199 pub const fn name(&self) -> &'p str {
200 self.name
201 }
202
203 #[must_use]
205 pub const fn name_raw(&self) -> &'p [u8] {
206 self.name_raw
207 }
208
209 #[must_use]
211 pub const fn kind(&self) -> PartKind {
212 self.kind
213 }
214
215 #[must_use]
217 pub const fn is_file(&self) -> bool {
218 matches!(self.kind, PartKind::File)
219 }
220
221 #[must_use]
223 pub const fn filename(&self) -> Option<FileName<'p>> {
224 self.filename
225 }
226
227 #[must_use]
229 pub fn content_type(&self) -> Option<ContentType<'p>> {
230 self.content_type.and_then(ContentType::parse)
231 }
232
233 #[must_use]
235 pub const fn charset(&self) -> Charset {
236 self.charset
237 }
238
239 #[must_use]
241 pub fn headers(&self) -> Headers<'p> {
242 Headers { rest: self.block }
243 }
244
245 #[must_use]
247 pub const fn header_bytes(&self) -> u32 {
248 self.block.len() as u32
249 }
250}
251
252#[derive(Clone, Copy)]
267pub struct ContentType<'p> {
268 raw: &'p [u8],
269 media: parse_net::MediaType<'p>,
270}
271
272impl fmt::Debug for ContentType<'_> {
273 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274 f.debug_struct("ContentType")
275 .field("type", &self.type_())
276 .field("subtype", &self.subtype())
277 .finish_non_exhaustive()
278 }
279}
280
281impl<'p> ContentType<'p> {
282 pub(crate) fn parse(raw: &'p [u8]) -> Option<ContentType<'p>> {
283 parse_net::media::parse(raw, &crate::kind::HEADER_LIMITS)
284 .ok()
285 .map(|media| ContentType { raw, media })
286 }
287
288 #[must_use]
290 pub const fn raw(&self) -> &'p [u8] {
291 self.raw
292 }
293
294 #[must_use]
296 pub fn type_(&self) -> &'p str {
297 core::str::from_utf8(self.media.type_()).unwrap_or("")
298 }
299
300 #[must_use]
302 pub fn subtype(&self) -> &'p str {
303 core::str::from_utf8(self.media.subtype()).unwrap_or("")
304 }
305
306 #[must_use]
308 pub fn is(&self, type_: &str, subtype: &str) -> bool {
309 self.media.is(type_, subtype)
310 }
311
312 #[must_use]
315 pub fn parameter(&self, name: &str) -> Option<&'p [u8]> {
316 self.media
317 .params()
318 .get(name)
319 .map(|w| crate::kind::word_raw(&w))
320 }
321
322 #[must_use]
324 pub fn charset(&self) -> Charset {
325 self.parameter("charset")
326 .map_or(Charset::Unspecified, Charset::from_label)
327 }
328}
329
330#[derive(Clone)]
335pub struct Headers<'p> {
336 rest: &'p [u8],
337}
338
339impl fmt::Debug for Headers<'_> {
340 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
341 let mut list = f.debug_list();
342 for (name, _) in self.clone() {
343 list.entry(&core::str::from_utf8(name).unwrap_or("?"));
344 }
345 list.finish()
346 }
347}
348
349impl<'p> Iterator for Headers<'p> {
350 type Item = (&'p [u8], &'p [u8]);
351
352 fn next(&mut self) -> Option<Self::Item> {
353 let lf = self.rest.iter().position(|&b| b == b'\n')?;
354 let line = self.rest.get(..lf.saturating_sub(1)).unwrap_or(&[]);
355 self.rest = self.rest.get(lf.wrapping_add(1)..).unwrap_or(&[]);
356 if line.is_empty() {
357 self.rest = &[];
358 return None;
359 }
360 let colon = line.iter().position(|&b| b == b':').unwrap_or(line.len());
361 let name = line.get(..colon).unwrap_or(&[]);
362 let value = line.get(colon.wrapping_add(1)..).unwrap_or(&[]);
363 Some((name, parse_net::text::trim_ows(value)))
364 }
365}
366
367#[derive(Clone, Copy, Debug, PartialEq, Eq)]
368enum State {
369 Preamble,
370 AfterDelimiter(After),
372 Headers,
373 Body,
374 EndOfPart,
376 Epilogue,
377 Poisoned,
378}
379
380#[derive(Clone, Copy, Debug, PartialEq, Eq)]
381enum After {
382 Start,
383 Dash,
384 Padding(u8),
385 Cr,
386}
387
388#[derive(Clone, Copy, Debug, PartialEq, Eq)]
390enum Token {
391 NotSeen,
392 Capturing,
393 Captured,
394}
395
396const SKIPPING: u8 = 1;
397const IS_TOKEN: u8 = 2;
398const IS_CHARSET: u8 = 4;
399const DEFERRED: u8 = 8;
400
401#[derive(Clone, Copy, Debug)]
403pub(crate) enum Source {
404 Input(usize),
406 Buffer,
408}
409
410#[derive(Clone, Copy, Debug)]
412pub(crate) enum Outcome {
413 PartStart(HeadFacts, Source),
414 DataInput(usize, usize),
416 DataDelimiter(usize),
418 PartEnd(PartSummary),
419 End,
420 NeedInput,
421}
422
423pub struct MultipartParser<B: AsMut<[u8]> + AsRef<[u8]>> {
471 boundary: Boundary,
472 limits: Limits,
473 policy: Policy,
474 buffer: B,
475 state: State,
476 k: u8,
478 bare: u8,
481 flags: u8,
482 token: Token,
483 token_len: u16,
484 charset_len: u8,
485 charset_done: bool,
486 scan: BlockScan,
487 copied: u32,
489 dirty: u32,
494 parts: u32,
495 files: u32,
496 fields: u32,
497 kind: PartKind,
498 part_bytes: u64,
499 text_total: u64,
500 data_start: u64,
501 preamble: u64,
502 epilogue: u64,
503 head_start: u64,
505 epilogue_start: u64,
507 bytes_seen: u64,
508}
509
510impl<B: AsMut<[u8]> + AsRef<[u8]>> fmt::Debug for MultipartParser<B> {
511 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
512 f.debug_struct("MultipartParser")
513 .field("boundary", &self.boundary)
514 .field("state", &self.state)
515 .field("bytes_seen", &self.bytes_seen)
516 .field("parts_seen", &self.parts)
517 .finish_non_exhaustive()
518 }
519}
520
521#[cfg(feature = "alloc")]
522impl MultipartParser<alloc::vec::Vec<u8>> {
523 pub fn new(boundary: Boundary, limits: &Limits, policy: &Policy) -> Result<Self, FormError> {
540 limits.validate_with(policy)?;
541 let buffer = alloc::vec![0u8; limits.multipart_buffer_len(policy)];
542 Self::with_buffer(boundary, limits, policy, buffer)
543 }
544}
545
546impl<B: AsMut<[u8]> + AsRef<[u8]>> MultipartParser<B> {
547 pub fn with_buffer(
566 boundary: Boundary,
567 limits: &Limits,
568 policy: &Policy,
569 buffer: B,
570 ) -> Result<Self, FormError> {
571 limits.validate_with(policy)?;
572 let needed = limits.multipart_buffer_len(policy);
573 if buffer.as_ref().len() < needed {
574 return Err(FormError::BufferTooSmall { needed });
575 }
576 Ok(Self {
577 boundary,
578 limits: *limits,
579 policy: *policy,
580 buffer,
581 state: State::Preamble,
582 k: 2,
585 bare: 1,
586 flags: 0,
587 token: Token::NotSeen,
588 token_len: 0,
589 charset_len: 0,
590 charset_done: false,
591 scan: BlockScan::default(),
592 copied: 0,
593 dirty: 0,
594 parts: 0,
595 files: 0,
596 fields: 0,
597 kind: PartKind::Field,
598 part_bytes: 0,
599 text_total: 0,
600 data_start: 0,
601 preamble: 0,
602 epilogue: 0,
603 head_start: 0,
604 epilogue_start: 0,
605 bytes_seen: 0,
606 })
607 }
608
609 pub fn parse<'p>(&'p mut self, input: &'p [u8], eof: bool) -> Result<Step<'p>, FormError> {
617 let (consumed, outcome) = self.advance(input, eof)?;
618 Ok(Step {
619 consumed,
620 event: self.view(outcome, input),
621 })
622 }
623
624 pub fn skip_part(&mut self) {
628 if matches!(self.state, State::Body) {
629 self.flags |= SKIPPING;
630 }
631 }
632
633 pub fn reset(&mut self, boundary: Boundary) {
636 self.wipe_used();
637 let limits = self.limits;
638 let policy = self.policy;
639 self.boundary = boundary;
640 self.state = State::Preamble;
641 self.k = 2;
642 self.bare = 1;
643 self.flags = 0;
644 self.token = Token::NotSeen;
645 self.token_len = 0;
646 self.charset_len = 0;
647 self.charset_done = false;
648 self.scan = BlockScan::default();
649 self.copied = 0;
650 self.parts = 0;
651 self.files = 0;
652 self.fields = 0;
653 self.kind = PartKind::Field;
654 self.part_bytes = 0;
655 self.text_total = 0;
656 self.data_start = 0;
657 self.preamble = 0;
658 self.epilogue = 0;
659 self.head_start = 0;
660 self.epilogue_start = 0;
661 self.bytes_seen = 0;
662 self.limits = limits;
663 self.policy = policy;
664 }
665
666 #[must_use]
668 pub const fn bytes_seen(&self) -> u64 {
669 self.bytes_seen
670 }
671
672 #[must_use]
674 pub const fn parts_seen(&self) -> u32 {
675 self.parts
676 }
677
678 #[must_use]
680 pub const fn files_seen(&self) -> u32 {
681 self.files
682 }
683
684 #[must_use]
686 pub fn is_done(&self) -> bool {
687 self.state == State::Epilogue
688 }
689
690 #[must_use]
692 pub const fn limits(&self) -> &Limits {
693 &self.limits
694 }
695
696 #[must_use]
698 pub const fn policy(&self) -> &Policy {
699 &self.policy
700 }
701
702 #[must_use]
704 pub fn captured_token(&self) -> Option<&str> {
705 if self.token != Token::Captured {
706 return None;
707 }
708 let start = self.token_area();
709 let bytes = self
710 .buffer
711 .as_ref()
712 .get(start..start.saturating_add(usize::from(self.token_len)))?;
713 core::str::from_utf8(bytes).ok()
714 }
715
716 #[must_use]
718 pub fn declared_charset(&self) -> Option<&[u8]> {
719 if !self.charset_done {
720 return None;
721 }
722 let start = self.charset_area();
723 self.buffer
724 .as_ref()
725 .get(start..start.saturating_add(usize::from(self.charset_len)))
726 }
727
728 #[cfg(feature = "alloc")]
730 pub(crate) fn area_name(&self, len: u32) -> &str {
731 let start = self.name_area();
732 let bytes = self
733 .buffer
734 .as_ref()
735 .get(start..start.saturating_add(len as usize))
736 .unwrap_or(&[]);
737 core::str::from_utf8(bytes).unwrap_or("")
738 }
739
740 #[cfg(feature = "alloc")]
742 pub(crate) fn area_filename(&self, len: u32) -> &[u8] {
743 let start = self.filename_area();
744 self.buffer
745 .as_ref()
746 .get(start..start.saturating_add(len as usize))
747 .unwrap_or(&[])
748 }
749
750 #[cfg(feature = "alloc")]
752 pub(crate) const fn data_start(&self) -> u64 {
753 self.data_start
754 }
755
756 fn name_area(&self) -> usize {
759 self.limits.max_part_header_bytes as usize
760 }
761
762 fn filename_area(&self) -> usize {
763 self.name_area().saturating_add(self.limits.name() as usize)
764 }
765
766 fn token_area(&self) -> usize {
767 let filename = if matches!(
768 self.policy.extended_filename,
769 crate::ExtendedFilenamePolicy::AcceptAlone
770 ) {
771 self.limits.filename() as usize
772 } else {
773 0
774 };
775 self.filename_area().saturating_add(filename)
776 }
777
778 fn charset_area(&self) -> usize {
779 let token = if self.policy.token_field.is_some() {
780 usize::from(self.limits.token())
781 } else {
782 0
783 };
784 self.token_area().saturating_add(token)
785 }
786
787 fn head_at(&self) -> Location {
789 Location::in_part(self.parts.checked_sub(1), self.head_start)
790 }
791
792 fn excess(&self, used: u64, limit: u64) -> Location {
795 let into_part = self.part_bytes.saturating_add(limit.saturating_sub(used));
796 Location::in_part(
797 self.parts.checked_sub(1),
798 self.data_start.saturating_add(into_part),
799 )
800 }
801
802 fn at(&self, pos: usize) -> Location {
803 self.at_offset(self.bytes_seen.saturating_add(pos as u64))
804 }
805
806 fn at_offset(&self, offset: u64) -> Location {
808 let part = if matches!(self.state, State::Preamble | State::Epilogue) {
809 None
810 } else {
811 self.parts.checked_sub(1)
812 };
813 Location::in_part(part, offset)
814 }
815
816 pub(crate) fn advance(
820 &mut self,
821 input: &[u8],
822 eof: bool,
823 ) -> Result<(usize, Outcome), FormError> {
824 if self.state == State::Poisoned {
825 return Err(FormError::Poisoned);
826 }
827 match self.run(input, eof) {
828 Ok((consumed, outcome)) => {
829 self.bytes_seen = self.bytes_seen.saturating_add(consumed as u64);
830 Ok((consumed, outcome))
831 }
832 Err(e) => {
833 self.state = State::Poisoned;
834 Err(e)
835 }
836 }
837 }
838
839 fn run(&mut self, input: &[u8], eof: bool) -> Result<(usize, Outcome), FormError> {
840 if self.bytes_seen.saturating_add(input.len() as u64) > self.limits.body() {
841 return Err(FormError::limit(
842 Limit::Body,
843 Location::at(self.limits.body()),
844 ));
845 }
846 let mut pos = 0usize;
847 loop {
848 let hay = input.get(pos..).unwrap_or(&[]);
849 match self.state {
850 State::Poisoned => return Err(FormError::Poisoned),
851 State::Preamble => {
852 if let Some(after) = self.preamble_step(hay)? {
853 pos = pos.wrapping_add(after);
854 self.state = State::AfterDelimiter(After::Start);
855 continue;
856 }
857 if eof {
858 return Err(FormError::syntax(
859 SyntaxError::NoInitialDelimiter,
860 self.at(input.len()),
861 ));
862 }
863 return Ok((input.len(), Outcome::NeedInput));
864 }
865 State::AfterDelimiter(sub) => {
866 let Some(&c) = hay.first() else {
867 return self.need_input(input.len(), eof);
868 };
869 pos = pos.wrapping_add(1);
870 let at = self.at(pos);
871 self.state = match (sub, c) {
872 (After::Start, b'-') => State::AfterDelimiter(After::Dash),
873 (After::Dash, b'-') => {
874 self.state = State::Epilogue;
875 self.epilogue_start = self.bytes_seen.saturating_add(pos as u64);
876 return Ok((pos, Outcome::End));
877 }
878 (After::Start, b' ' | b'\t') => {
879 if self.limits.max_padding_bytes == 0 {
880 return Err(FormError::limit(Limit::Padding, at));
881 }
882 State::AfterDelimiter(After::Padding(1))
883 }
884 (After::Padding(n), b' ' | b'\t') => {
885 if n >= self.limits.max_padding_bytes {
886 return Err(FormError::limit(Limit::Padding, at));
887 }
888 State::AfterDelimiter(After::Padding(n.saturating_add(1)))
889 }
890 (After::Start | After::Padding(_), b'\r') => {
891 State::AfterDelimiter(After::Cr)
892 }
893 (After::Cr, b'\n') => {
894 self.parts = self.parts.saturating_add(1);
895 if self.parts > self.limits.max_parts {
896 return Err(FormError::limit(Limit::Parts, self.at(pos)));
897 }
898 self.scan = BlockScan::default();
899 self.copied = 0;
900 self.head_start = self.bytes_seen.saturating_add(pos as u64);
901 State::Headers
902 }
903 (After::Start | After::Padding(_), b'\n') => {
904 return Err(FormError::syntax(SyntaxError::BareLineFeed, at));
905 }
906 _ => return Err(FormError::syntax(SyntaxError::BoundaryInContent, at)),
907 };
908 }
909 State::Headers => {
910 let at = self.head_at();
911 match self.scan.feed(hay, &self.limits, at)? {
912 Some(end) => {
913 let source = if self.copied == 0 {
914 Source::Input(pos)
915 } else {
916 self.copy_header(hay.get(..end).unwrap_or(&[]), at)?;
917 Source::Buffer
918 };
919 let block_start_in_input = pos;
920 pos = pos.wrapping_add(end);
921 let facts =
922 self.read_head(input, source, block_start_in_input, end, at)?;
923 let (is_token, is_charset) = {
924 let block = self.block(&facts, source, input);
925 let name = self.name_bytes(&facts, block);
926 let token = self
927 .policy
928 .token_field
929 .is_some_and(|t| name == t.name().as_bytes());
930 (token, name == b"_charset_")
931 };
932 self.start_part(&facts, is_token, is_charset, at)?;
933 self.data_start = self.bytes_seen.saturating_add(pos as u64);
934 return Ok((pos, Outcome::PartStart(facts, source)));
935 }
936 None => {
937 self.copy_header(hay, at)?;
938 return self.need_input(input.len(), eof);
939 }
940 }
941 }
942 State::Body => {
943 if hay.is_empty() {
944 return self.need_input(input.len(), eof);
945 }
946 match self.body_step(hay, pos, input.len(), eof)? {
947 BodyStep::Emit(consumed, outcome) => return Ok((consumed, outcome)),
948 BodyStep::Continue(next) => pos = next,
949 }
950 }
951 State::EndOfPart => {
952 let summary = self.end_part(self.at(pos))?;
953 self.state = State::AfterDelimiter(After::Start);
954 return Ok((pos, Outcome::PartEnd(summary)));
955 }
956 State::Epilogue => {
957 self.epilogue = self.epilogue.saturating_add(hay.len() as u64);
958 if self.epilogue > self.limits.epilogue() {
959 let at = self.epilogue_start.saturating_add(self.limits.epilogue());
960 return Err(FormError::limit(Limit::Epilogue, Location::at(at)));
961 }
962 return Ok((input.len(), Outcome::End));
963 }
964 }
965 }
966 }
967
968 fn need_input(&self, len: usize, eof: bool) -> Result<(usize, Outcome), FormError> {
969 if eof {
970 Err(FormError::syntax(SyntaxError::Truncated, self.at(len)))
971 } else {
972 Ok((len, Outcome::NeedInput))
973 }
974 }
975
976 fn bare_break(&self, after: usize, pos: usize) -> FormError {
980 let end = self
981 .bytes_seen
982 .saturating_add(pos.wrapping_add(after) as u64);
983 let bare = self.boundary.len().saturating_sub(1) as u64;
984 FormError::syntax(
985 SyntaxError::BoundaryInContent,
986 self.at_offset(end.saturating_sub(bare)),
987 )
988 }
989
990 fn preamble_step(&mut self, hay: &[u8]) -> Result<Option<usize>, FormError> {
993 let len = self.boundary.len() as u64;
994 let budget = self.limits.preamble().saturating_add(len);
996 let room = usize::try_from(budget.saturating_sub(self.preamble)).unwrap_or(usize::MAX);
997 let window = hay.get(..room.min(hay.len())).unwrap_or(hay);
998 let (k, m, found) = self
999 .boundary
1000 .run(usize::from(self.k), usize::from(self.bare), window);
1001 self.k = k as u8;
1002 self.bare = m as u8;
1003 match found {
1004 Hit::BareBreak(after) => Err(self.bare_break(after, 0)),
1005 Hit::Delimiter(after) => {
1006 let end = self.preamble.saturating_add(after as u64);
1009 let preamble = end.saturating_sub(len);
1010 if preamble > self.limits.preamble() {
1011 return Err(FormError::limit(
1012 Limit::Preamble,
1013 Location::at(self.limits.preamble()),
1014 ));
1015 }
1016 self.k = 0;
1017 self.bare = 0;
1018 Ok(Some(after))
1019 }
1020 Hit::None => {
1021 self.preamble = self.preamble.saturating_add(window.len() as u64);
1022 if window.len() < hay.len() || self.preamble >= budget {
1023 return Err(FormError::limit(
1024 Limit::Preamble,
1025 Location::at(self.limits.preamble()),
1026 ));
1027 }
1028 Ok(None)
1029 }
1030 }
1031 }
1032
1033 fn copy_header(&mut self, bytes: &[u8], at: Location) -> Result<(), FormError> {
1034 let start = self.copied as usize;
1035 let end = start.saturating_add(bytes.len());
1036 if end > self.limits.max_part_header_bytes as usize {
1037 return Err(FormError::limit(Limit::PartHeaderBytes, at));
1038 }
1039 match self.buffer.as_mut().get_mut(start..end) {
1040 Some(dst) => dst.copy_from_slice(bytes),
1041 None => return Err(FormError::limit(Limit::PartHeaderBytes, at)),
1042 }
1043 self.copied = end as u32;
1044 self.dirty = self.dirty.max(self.copied);
1045 Ok(())
1046 }
1047
1048 fn read_head(
1049 &mut self,
1050 input: &[u8],
1051 source: Source,
1052 start: usize,
1053 end: usize,
1054 at: Location,
1055 ) -> Result<HeadFacts, FormError> {
1056 let name_area = self.name_area();
1057 let filename_area = self.filename_area();
1058 let filename_end = self.token_area();
1059 let limits = self.limits;
1060 let policy = self.policy;
1061 let copied = self.copied as usize;
1062 let block_len = match source {
1063 Source::Input(_) => end,
1064 Source::Buffer => copied,
1065 };
1066 self.dirty = self.dirty.max(u32::try_from(block_len).unwrap_or(u32::MAX));
1067 let buf = self.buffer.as_mut();
1068 let (head, rest) = buf.split_at_mut(name_area.min(buf.len()));
1069 let (name, rest) =
1070 rest.split_at_mut(filename_area.saturating_sub(name_area).min(rest.len()));
1071 let filename = rest
1072 .get_mut(..filename_end.saturating_sub(filename_area))
1073 .unwrap_or(&mut []);
1074 let block = match source {
1075 Source::Input(_) => input.get(start..start.saturating_add(end)).unwrap_or(&[]),
1076 Source::Buffer => head.get(..copied).unwrap_or(&[]),
1077 };
1078 read_block(block, &limits, &policy, Areas { name, filename }, at)
1079 }
1080
1081 fn start_part(
1083 &mut self,
1084 facts: &HeadFacts,
1085 is_token: bool,
1086 is_charset: bool,
1087 at: Location,
1088 ) -> Result<(), FormError> {
1089 self.state = State::Body;
1090 self.k = 0;
1091 self.bare = 1;
1094 self.flags = 0;
1095 self.kind = facts.kind;
1096 self.part_bytes = 0;
1097 match facts.kind {
1098 PartKind::Field => {
1099 self.fields = self.fields.saturating_add(1);
1100 if self.fields > self.limits.max_fields {
1101 return Err(FormError::limit(Limit::Fields, at));
1102 }
1103 if is_token {
1104 if self.token != Token::NotSeen {
1105 return Err(FormError::policy(PolicyRefusal::DuplicateToken, at));
1106 }
1107 self.token = Token::Capturing;
1108 self.token_len = 0;
1109 self.flags |= IS_TOKEN;
1110 }
1111 if is_charset {
1112 self.flags |= IS_CHARSET;
1113 self.charset_len = 0;
1114 self.charset_done = false;
1115 }
1116 }
1117 PartKind::File => {
1118 let empty = matches!(facts.filename, Some((Text::Block(span), _)) if span.len == 0)
1119 || matches!(facts.filename, Some((Text::Area(0), _)));
1120 if empty && self.policy.empty_files == EmptyFilePolicy::Skip {
1121 self.flags |= DEFERRED;
1122 } else {
1123 self.count_file(at)?;
1124 }
1125 }
1126 }
1127 Ok(())
1128 }
1129
1130 fn block<'a>(&'a self, facts: &HeadFacts, source: Source, input: &'a [u8]) -> &'a [u8] {
1132 let len = facts.block_len as usize;
1133 match source {
1134 Source::Input(start) => input.get(start..start.saturating_add(len)).unwrap_or(&[]),
1135 Source::Buffer => self.buffer.as_ref().get(..len).unwrap_or(&[]),
1136 }
1137 }
1138
1139 fn name_bytes<'a>(&'a self, facts: &HeadFacts, block: &'a [u8]) -> &'a [u8] {
1141 match facts.name {
1142 Text::Block(span) => span.get(block),
1143 Text::Area(len) => {
1144 let start = self.name_area();
1145 self.buffer
1146 .as_ref()
1147 .get(start..start.saturating_add(len as usize))
1148 .unwrap_or(&[])
1149 }
1150 }
1151 }
1152
1153 fn head_view<'p>(&'p self, facts: &HeadFacts, source: Source, input: &'p [u8]) -> PartHead<'p> {
1155 let block = self.block(facts, source, input);
1156 let name = core::str::from_utf8(self.name_bytes(facts, block)).unwrap_or("");
1157 let filename = facts.filename.map(|(text, origin)| {
1158 let raw = match text {
1159 Text::Block(span) => span.get(block),
1160 Text::Area(len) => {
1161 let start = self.filename_area();
1162 self.buffer
1163 .as_ref()
1164 .get(start..start.saturating_add(len as usize))
1165 .unwrap_or(&[])
1166 }
1167 };
1168 FileName::new(raw, origin)
1169 });
1170 PartHead {
1171 index: self.parts.saturating_sub(1),
1172 block,
1173 name,
1174 name_raw: facts.name_raw.get(block),
1175 kind: facts.kind,
1176 filename,
1177 content_type: facts.content_type.map(|span| span.get(block)),
1178 charset: facts.charset,
1179 }
1180 }
1181
1182 fn view<'p>(&'p self, outcome: Outcome, input: &'p [u8]) -> Event<'p> {
1184 match outcome {
1185 Outcome::PartStart(facts, source) => {
1186 Event::PartStart(self.head_view(&facts, source, input))
1187 }
1188 Outcome::DataInput(start, end) => Event::Data(input.get(start..end).unwrap_or(&[])),
1189 Outcome::DataDelimiter(n) => {
1190 Event::Data(self.boundary.delimiter().get(..n).unwrap_or(&[]))
1191 }
1192 Outcome::PartEnd(summary) => Event::PartEnd(summary),
1193 Outcome::End => Event::End,
1194 Outcome::NeedInput => Event::NeedInput,
1195 }
1196 }
1197
1198 fn count_file(&mut self, at: Location) -> Result<(), FormError> {
1199 self.files = self.files.saturating_add(1);
1200 if self.files > self.limits.max_files {
1201 return Err(FormError::limit(Limit::Files, at));
1202 }
1203 if let Some(t) = self.policy.token_field {
1204 if t.placement() == TokenPlacement::BeforeFirstFile && self.token != Token::Captured {
1205 return Err(FormError::policy(PolicyRefusal::TokenFieldLate, at));
1206 }
1207 }
1208 Ok(())
1209 }
1210
1211 fn take_data(&mut self, bytes: &[u8]) -> Result<(), FormError> {
1214 let n = bytes.len() as u64;
1215 if n == 0 {
1216 return Ok(());
1217 }
1218 if self.flags & DEFERRED != 0 {
1219 self.flags &= !DEFERRED;
1220 let at = Location::in_part(self.parts.checked_sub(1), self.data_start);
1221 self.count_file(at)?;
1222 }
1223 let total = self.part_bytes.saturating_add(n);
1224 match self.kind {
1225 PartKind::File => {
1226 let limit = self.limits.file();
1227 if total > limit {
1228 let at = self.excess(self.part_bytes, limit);
1229 return Err(FormError::limit(Limit::FileSize, at));
1230 }
1231 }
1232 PartKind::Field => {
1233 let limit = u64::from(self.limits.value());
1234 if total > limit {
1235 let at = self.excess(self.part_bytes, limit);
1236 return Err(FormError::limit(Limit::ValueLength, at));
1237 }
1238 let text = self.text_total.saturating_add(n);
1239 let limit = self.limits.text();
1240 if text > limit {
1241 let at = self.excess(self.text_total, limit);
1242 return Err(FormError::limit(Limit::TextTotal, at));
1243 }
1244 self.text_total = text;
1245 }
1246 }
1247 if self.flags & IS_TOKEN != 0 {
1248 let start = self
1249 .token_area()
1250 .saturating_add(usize::from(self.token_len));
1251 let end = start.saturating_add(bytes.len());
1252 let limit = u64::from(self.limits.token());
1253 let at = self.excess(u64::from(self.token_len), limit);
1254 if end
1255 > self
1256 .token_area()
1257 .saturating_add(usize::from(self.limits.token()))
1258 {
1259 return Err(FormError::limit(Limit::TokenLength, at));
1260 }
1261 match self.buffer.as_mut().get_mut(start..end) {
1262 Some(dst) => dst.copy_from_slice(bytes),
1263 None => return Err(FormError::limit(Limit::TokenLength, at)),
1264 }
1265 self.token_len = self.token_len.saturating_add(bytes.len() as u16);
1266 }
1267 if self.flags & IS_CHARSET != 0 {
1268 let start = self
1269 .charset_area()
1270 .saturating_add(usize::from(self.charset_len));
1271 let end = start.saturating_add(bytes.len());
1272 let at = self.excess(u64::from(self.charset_len), CHARSET_CAPTURE as u64);
1273 if end > self.charset_area().saturating_add(CHARSET_CAPTURE) {
1274 return Err(FormError::encoding(EncodingError::UnsupportedCharset, at));
1275 }
1276 match self.buffer.as_mut().get_mut(start..end) {
1277 Some(dst) => dst.copy_from_slice(bytes),
1278 None => return Err(FormError::encoding(EncodingError::UnsupportedCharset, at)),
1279 }
1280 self.charset_len = self.charset_len.saturating_add(bytes.len() as u8);
1281 }
1282 self.part_bytes = total;
1283 Ok(())
1284 }
1285
1286 fn end_part(&mut self, at: Location) -> Result<PartSummary, FormError> {
1287 if self.flags & IS_TOKEN != 0 {
1288 let start = self.token_area();
1289 let bytes = self
1290 .buffer
1291 .as_ref()
1292 .get(start..start.saturating_add(usize::from(self.token_len)))
1293 .unwrap_or(&[]);
1294 if core::str::from_utf8(bytes).is_err() {
1295 return Err(FormError::encoding(EncodingError::InvalidUtf8, at));
1296 }
1297 self.token = Token::Captured;
1298 }
1299 if self.flags & IS_CHARSET != 0 {
1300 self.charset_done = true;
1301 }
1302 let summary = PartSummary {
1303 index: self.parts.saturating_sub(1),
1304 kind: self.kind,
1305 bytes: self.part_bytes,
1306 skipped: self.flags & SKIPPING != 0,
1307 };
1308 self.flags = 0;
1309 self.k = 0;
1310 Ok(summary)
1311 }
1312
1313 fn body_step(
1315 &mut self,
1316 hay: &[u8],
1317 pos: usize,
1318 input_len: usize,
1319 eof: bool,
1320 ) -> Result<BodyStep, FormError> {
1321 let len = self.boundary.len();
1322 let k0 = usize::from(self.k);
1323 if k0 > 0 {
1324 let need = self.boundary.delimiter().get(k0..).unwrap_or(&[]);
1328 let (need_len, n) = (need.len(), super::boundary::shared(need, hay));
1329 if n == need_len {
1330 self.k = 0;
1332 self.bare = 0;
1333 self.state = State::EndOfPart;
1334 return self.emit_delimiter_bytes(0, pos.wrapping_add(n));
1335 }
1336 if n < hay.len() {
1337 self.k = 0;
1341 self.bare = k0.max(2).wrapping_sub(1) as u8;
1342 return self.emit_delimiter_bytes(k0, pos);
1343 }
1344 if eof {
1346 return Err(FormError::syntax(
1347 SyntaxError::Truncated,
1348 self.at(input_len),
1349 ));
1350 }
1351 self.k = k0.saturating_add(n) as u8;
1352 return self.emit_delimiter_bytes(0, input_len);
1353 }
1354 let (k, m, found) = self.boundary.run(0, usize::from(self.bare), hay);
1355 match found {
1356 Hit::BareBreak(after) => Err(self.bare_break(after, pos)),
1357 Hit::Delimiter(after) => {
1358 let data = after.saturating_sub(len);
1359 self.k = 0;
1360 self.bare = 0;
1361 self.state = State::EndOfPart;
1362 let bytes = hay.get(..data).unwrap_or(&[]);
1363 self.emit_input_bytes(bytes, pos, pos.wrapping_add(data), pos.wrapping_add(after))
1364 }
1365 Hit::None => {
1366 if eof {
1367 return Err(FormError::syntax(
1368 SyntaxError::Truncated,
1369 self.at(input_len),
1370 ));
1371 }
1372 self.k = k as u8;
1373 self.bare = m as u8;
1374 let data_end = hay.len().saturating_sub(k);
1375 let bytes = hay.get(..data_end).unwrap_or(&[]);
1376 self.emit_input_bytes(bytes, pos, pos.wrapping_add(data_end), input_len)
1377 }
1378 }
1379 }
1380
1381 fn emit_delimiter_bytes(&mut self, n: usize, consumed: usize) -> Result<BodyStep, FormError> {
1384 let boundary = self.boundary;
1385 let bytes = boundary.delimiter().get(..n).unwrap_or(&[]);
1386 self.take_data(bytes)?;
1387 if n == 0 || self.flags & SKIPPING != 0 {
1388 return Ok(BodyStep::Continue(consumed));
1389 }
1390 Ok(BodyStep::Emit(consumed, Outcome::DataDelimiter(n)))
1391 }
1392
1393 fn emit_input_bytes(
1396 &mut self,
1397 bytes: &[u8],
1398 start: usize,
1399 end: usize,
1400 consumed: usize,
1401 ) -> Result<BodyStep, FormError> {
1402 self.take_data(bytes)?;
1403 if start == end || self.flags & SKIPPING != 0 {
1404 return Ok(BodyStep::Continue(consumed));
1405 }
1406 Ok(BodyStep::Emit(consumed, Outcome::DataInput(start, end)))
1407 }
1408}
1409
1410enum BodyStep {
1412 Emit(usize, Outcome),
1413 Continue(usize),
1414}
1415
1416impl<B: AsMut<[u8]> + AsRef<[u8]>> MultipartParser<B> {
1417 fn wipe_used(&mut self) {
1421 let used = self.dirty as usize;
1422 let regions = [
1423 (0, used.min(self.name_area())),
1424 (
1425 self.name_area(),
1426 used.min(self.filename_area().saturating_sub(self.name_area())),
1427 ),
1428 (
1429 self.filename_area(),
1430 used.min(self.token_area().saturating_sub(self.filename_area())),
1431 ),
1432 (self.token_area(), usize::from(self.token_len)),
1433 (self.charset_area(), CHARSET_CAPTURE),
1436 ];
1437 let buf = self.buffer.as_mut();
1438 for (start, len) in regions {
1439 if let Some(region) = buf.get_mut(start..start.saturating_add(len)) {
1440 crate::wipe::wipe(region);
1441 }
1442 }
1443 self.dirty = 0;
1444 }
1445}
1446
1447impl<B: AsMut<[u8]> + AsRef<[u8]>> Drop for MultipartParser<B> {
1448 fn drop(&mut self) {
1449 self.wipe_used();
1450 }
1451}
1452
1453#[cfg(test)]
1454mod tests {
1455 use super::*;
1456
1457 #[test]
1462 fn test_wipe_covers_every_area_written() {
1463 let policy = Policy::DEFAULT
1464 .with_extended_filename(crate::ExtendedFilenamePolicy::AcceptAlone)
1465 .with_token_field(crate::TokenField::new("csrf"));
1466 let limits = Limits::DEFAULT.with_max_part_header_bytes(512);
1467 let long = "x".repeat(300);
1468 let body = alloc::format!(
1469 "--X\r\nContent-Disposition: form-data; name=csrf\r\n\r\nsecret-token-value\r\n\
1470 --X\r\nContent-Disposition: form-data; name=_charset_\r\n\r\nUTF-8\r\n\
1471 --X\r\nContent-Disposition: form-data; name=\"pass%22word\"\r\nX-Pad: {long}\r\n\r\nhunter2\r\n\
1472 --X\r\nContent-Disposition: form-data; name=f; filename*=UTF-8''caf%C3%A9.txt\r\n\r\nfile\r\n--X--"
1473 );
1474 let body = body.replace(
1477 "name=_charset_\r\n\r\nUTF-8\r\n",
1478 "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",
1479 );
1480 assert!(body.contains("unicode-1-1-utf-8"));
1481 for split in [usize::MAX, 7, 1] {
1482 let mut buf = alloc::vec![0u8; limits.multipart_buffer_len(&policy)];
1483 {
1484 let mut p = MultipartParser::with_buffer(
1485 Boundary::new(b"X").unwrap(),
1486 &limits,
1487 &policy,
1488 &mut buf[..],
1489 )
1490 .unwrap();
1491 let bytes = body.as_bytes();
1492 let chunks: alloc::vec::Vec<&[u8]> = bytes.chunks(split.min(bytes.len())).collect();
1493 let mut ended = false;
1494 for (i, c) in chunks.iter().enumerate() {
1495 let last = i + 1 == chunks.len();
1496 let mut rest = *c;
1497 loop {
1498 let step = p.parse(rest, last).unwrap();
1499 rest = &rest[step.consumed..];
1500 match step.event {
1501 Event::NeedInput => break,
1502 Event::End => {
1503 ended = true;
1504 break;
1505 }
1506 _ => {}
1507 }
1508 }
1509 }
1510 assert!(ended);
1511 assert_eq!(p.captured_token(), Some("secret-token-value"));
1512 }
1513 assert!(
1514 buf.iter().all(|&b| b == 0),
1515 "split {split}: bytes left at {:?}",
1516 buf.iter().position(|&b| b != 0)
1517 );
1518 }
1519 }
1520
1521 #[cfg(feature = "alloc")]
1523 fn run(p: &mut MultipartParser<alloc::vec::Vec<u8>>, body: &[u8]) -> Option<FormError> {
1524 let mut rest = body;
1525 loop {
1526 match p.parse(rest, true) {
1527 Err(e) => return Some(e),
1528 Ok(step) => {
1529 rest = &rest[step.consumed..];
1530 if matches!(step.event, Event::End) {
1531 return None;
1532 }
1533 }
1534 }
1535 }
1536 }
1537
1538 #[cfg(feature = "alloc")]
1542 #[test]
1543 fn test_attack_t28_counters_near_u64_max_never_wrap() {
1544 let body = b"--X\r\nContent-Disposition: form-data; name=a\r\n\r\nvalue\r\n--X--";
1545 let new = |limits: &Limits| {
1546 MultipartParser::new(Boundary::new(b"X").unwrap(), limits, &Policy::DEFAULT).unwrap()
1547 };
1548 let mut p = new(&Limits::DEFAULT);
1549 p.bytes_seen = u64::MAX - 4;
1550 assert!(matches!(
1551 run(&mut p, body),
1552 Some(FormError::Limit {
1553 limit: crate::error::Limit::Body,
1554 ..
1555 })
1556 ));
1557
1558 let mut p = new(&Limits::DEFAULT);
1559 p.text_total = u64::MAX - 2;
1560 assert!(matches!(
1561 run(&mut p, body),
1562 Some(FormError::Limit {
1563 limit: crate::error::Limit::TextTotal,
1564 ..
1565 })
1566 ));
1567
1568 let open = Limits::DEFAULT
1569 .with_max_body_bytes(u64::MAX)
1570 .with_max_text_bytes(u64::MAX);
1571 let mut p = new(&open);
1572 p.bytes_seen = u64::MAX - 4;
1573 assert!(run(&mut p, body).is_none());
1574 assert_eq!(p.bytes_seen, u64::MAX);
1575 }
1576
1577 #[test]
1578 fn test_parser_size_is_bounded() {
1579 assert!(core::mem::size_of::<MultipartParser<&mut [u8]>>() <= 384);
1582 assert!(core::mem::size_of::<Boundary>() <= 150);
1583 }
1584
1585 #[cfg(feature = "alloc")]
1586 #[test]
1587 fn test_debug_never_shows_data_or_file_names() {
1588 let body = b"--X\r\nContent-Disposition: form-data; name=f; filename=\"secret.pdf\"\r\n\r\nhunter2\r\n--X--";
1589 let mut p = MultipartParser::new(
1590 Boundary::new(b"X").unwrap(),
1591 &Limits::DEFAULT,
1592 &Policy::DEFAULT,
1593 )
1594 .unwrap();
1595 let mut rest: &[u8] = body;
1596 let mut seen = alloc::string::String::new();
1597 loop {
1598 let step = p.parse(rest, true).unwrap();
1599 rest = &rest[step.consumed..];
1600 if let Event::PartStart(head) = &step.event {
1601 seen.push_str(&alloc::format!("{:?}", head.headers()));
1602 }
1603 seen.push_str(&alloc::format!("{step:?}"));
1604 if matches!(step.event, Event::End) {
1605 break;
1606 }
1607 }
1608 assert!(seen.contains("Data(7 bytes)"), "{seen}");
1609 assert!(
1610 !seen.contains("hunter2") && !seen.contains("secret"),
1611 "{seen}"
1612 );
1613 }
1614
1615 #[test]
1616 fn test_charset_labels() {
1617 assert_eq!(Charset::from_label(b"UTF-8"), Charset::Utf8);
1618 assert_eq!(Charset::from_label(b"us-ascii"), Charset::Ascii);
1619 assert_eq!(Charset::from_label(b"utf8"), Charset::Other);
1620 }
1621}