Skip to main content

git_zero_server/receive/
control.rs

1use super::Capabilities;
2use git_zero_core::{
3    Error, ObjectFormat, ObjectId, Result, pktline, repository::validate_ref_name_bytes,
4};
5
6#[derive(Clone, Copy, Debug, Eq, PartialEq)]
7pub struct Command<'a> {
8    pub old: ObjectId,
9    pub new: ObjectId,
10    pub name: &'a [u8],
11}
12
13#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14pub struct Limits {
15    pub max_bytes: u64,
16    pub max_commands: u32,
17    pub max_shallow: u32,
18    pub max_options: u32,
19}
20
21#[derive(Clone, Copy, Debug, Eq, PartialEq)]
22pub enum ControlEvent<'a> {
23    NeedInput,
24    Shallow(ObjectId),
25    Command(Command<'a>),
26    /// The command flush was read; negotiated push options follow next.
27    Options,
28    PushOption(&'a [u8]),
29    /// The input suffix is untouched. Pass it to pack decoding when required.
30    End {
31        pack_required: bool,
32    },
33}
34
35#[derive(Clone, Copy, Debug, Eq, PartialEq)]
36pub struct ControlProgress<'a> {
37    pub consumed: usize,
38    pub event: ControlEvent<'a>,
39}
40
41#[derive(Clone, Copy, Eq, PartialEq)]
42enum Phase {
43    Commands,
44    Options,
45    End,
46}
47
48/// Incremental receive-pack control decoding with one caller-owned packet
49/// buffer. Persist each borrowed command/option before calling `read` again.
50/// No pack bytes are consumed. Duplicate refs, old-value CAS, fast-forwards,
51/// authorization and shallow-update policy are host validation responsibilities.
52pub struct ControlDecoder<'a> {
53    frame: Frame<'a>,
54    state: ControlState,
55    failure: Option<Error>,
56}
57
58struct ControlState {
59    format: ObjectFormat,
60    offered: Capabilities,
61    selected: Capabilities,
62    limits: Limits,
63    phase: Phase,
64    commands: u32,
65    shallow: u32,
66    options: u32,
67    pack_required: bool,
68}
69
70impl<'a> ControlDecoder<'a> {
71    pub fn new(
72        storage: &'a mut [u8],
73        format: ObjectFormat,
74        offered: Capabilities,
75        limits: Limits,
76    ) -> Result<Self> {
77        if storage.len() < 4 {
78            return Err(Error::BufferTooSmall);
79        }
80        Ok(Self {
81            frame: Frame {
82                storage,
83                filled: 0,
84                target: 4,
85                ready: false,
86                total: 0,
87                limit: limits.max_bytes,
88            },
89            state: ControlState {
90                format,
91                offered,
92                selected: Capabilities::default(),
93                limits,
94                phase: Phase::Commands,
95                commands: 0,
96                shallow: 0,
97                options: 0,
98                pack_required: false,
99            },
100            failure: None,
101        })
102    }
103
104    pub fn capabilities(&self) -> Capabilities {
105        self.state.selected
106    }
107
108    /// EOF before the last control flush is a terminal error. `End` refers only
109    /// to the control section; the host must still require and validate the pack
110    /// or require transport EOF for a deletion-only/no-op request.
111    pub fn read(&mut self, input: &[u8], end_of_input: bool) -> Result<ControlProgress<'_>> {
112        if let Some(error) = self.failure {
113            return Err(error);
114        }
115        if self.state.phase == Phase::End {
116            return Ok(ControlProgress {
117                consumed: 0,
118                event: ControlEvent::End {
119                    pack_required: self.state.pack_required,
120                },
121            });
122        }
123        let frame = match self.frame.read(input) {
124            Ok(frame) => frame,
125            Err(error) => {
126                self.failure = Some(error);
127                return Err(error);
128            }
129        };
130        let event = match frame.event {
131            FrameEvent::NeedInput if end_of_input => Err(Error::InvalidPacket),
132            event => self.state.accept(event),
133        };
134        match event {
135            Ok(event) => Ok(ControlProgress {
136                consumed: frame.consumed,
137                event,
138            }),
139            Err(error) => {
140                self.failure = Some(error);
141                Err(error)
142            }
143        }
144    }
145}
146
147impl ControlState {
148    fn accept<'a>(&mut self, event: FrameEvent<'a>) -> Result<ControlEvent<'a>> {
149        match event {
150            FrameEvent::NeedInput => Ok(ControlEvent::NeedInput),
151            FrameEvent::Flush => Ok(self.flush()),
152            FrameEvent::Data(bytes) if self.phase == Phase::Options => self.option(bytes),
153            FrameEvent::Data(bytes) => {
154                let bytes = bytes.strip_suffix(b"\n").unwrap_or(bytes);
155                if let Some(hex) = bytes.strip_prefix(b"shallow ") {
156                    self.boundary(hex)
157                } else {
158                    self.command(bytes)
159                }
160            }
161        }
162    }
163
164    fn flush(&mut self) -> ControlEvent<'static> {
165        if self.phase == Phase::Commands && self.selected.contains(Capabilities::PUSH_OPTIONS) {
166            self.phase = Phase::Options;
167            ControlEvent::Options
168        } else {
169            self.phase = Phase::End;
170            ControlEvent::End {
171                pack_required: self.pack_required,
172            }
173        }
174    }
175
176    fn option<'a>(&mut self, bytes: &'a [u8]) -> Result<ControlEvent<'a>> {
177        if self.options == self.limits.max_options {
178            return Err(Error::LimitExceeded);
179        }
180        if bytes.iter().any(|byte| matches!(byte, 0 | b'\n' | b'\r')) {
181            return Err(Error::InvalidRequest);
182        }
183        self.options += 1;
184        Ok(ControlEvent::PushOption(bytes))
185    }
186
187    fn boundary(&mut self, hex: &[u8]) -> Result<ControlEvent<'static>> {
188        if self.commands != 0 {
189            return Err(Error::InvalidRequest);
190        }
191        if self.shallow == self.limits.max_shallow {
192            return Err(Error::LimitExceeded);
193        }
194        let id = object_id(hex, self.format)?;
195        if id.is_zero() {
196            return Err(Error::InvalidRequest);
197        }
198        self.shallow += 1;
199        Ok(ControlEvent::Shallow(id))
200    }
201
202    fn command<'a>(&mut self, bytes: &'a [u8]) -> Result<ControlEvent<'a>> {
203        if self.commands == self.limits.max_commands {
204            return Err(Error::LimitExceeded);
205        }
206        let (command, capabilities) =
207            command(bytes, self.format, self.commands == 0, self.offered)?;
208        if command.new.is_zero() && !self.offered.contains(Capabilities::DELETE_REFS) {
209            return Err(Error::Unsupported);
210        }
211        if self.commands == 0 {
212            self.selected = capabilities;
213        }
214        self.pack_required |= !command.new.is_zero();
215        self.commands += 1;
216        Ok(ControlEvent::Command(command))
217    }
218}
219
220fn object_id(bytes: &[u8], format: ObjectFormat) -> Result<ObjectId> {
221    let id = ObjectId::from_hex(bytes).map_err(|_| Error::InvalidRequest)?;
222    if id.format() != format {
223        return Err(Error::InvalidRequest);
224    }
225    Ok(id)
226}
227
228fn command(
229    bytes: &[u8],
230    format: ObjectFormat,
231    first: bool,
232    offered: Capabilities,
233) -> Result<(Command<'_>, Capabilities)> {
234    let (line, selected) = match bytes.iter().position(|&byte| byte == 0) {
235        Some(at) if first => (
236            &bytes[..at],
237            Capabilities::parse(&bytes[at + 1..], format, offered)?,
238        ),
239        Some(_) => return Err(Error::InvalidRequest),
240        None if first && format == ObjectFormat::Sha256 => return Err(Error::InvalidRequest),
241        None => (bytes, Capabilities::default()),
242    };
243    let width = format.hex_length();
244    if line.get(width) != Some(&b' ') || line.get(width * 2 + 1) != Some(&b' ') {
245        return Err(Error::InvalidRequest);
246    }
247    let old = object_id(&line[..width], format)?;
248    let new = object_id(&line[width + 1..width * 2 + 1], format)?;
249    let name = &line[width * 2 + 2..];
250    validate_ref_name_bytes(name)?;
251    if !new.is_zero() {
252        super::advertisement::validate_first_name(format, offered, name)?;
253    }
254    Ok((Command { old, new, name }, selected))
255}
256
257enum FrameEvent<'a> {
258    NeedInput,
259    Flush,
260    Data(&'a [u8]),
261}
262struct FrameProgress<'a> {
263    consumed: usize,
264    event: FrameEvent<'a>,
265}
266struct Frame<'a> {
267    storage: &'a mut [u8],
268    filled: usize,
269    target: usize,
270    ready: bool,
271    total: u64,
272    limit: u64,
273}
274
275impl Frame<'_> {
276    fn read(&mut self, input: &[u8]) -> Result<FrameProgress<'_>> {
277        if self.ready {
278            self.filled = 0;
279            self.target = 4;
280            self.ready = false;
281        }
282        let mut consumed = 0;
283        while self.filled < self.target {
284            let count = (self.target - self.filled).min(input.len() - consumed);
285            if count == 0 {
286                return Ok(FrameProgress {
287                    consumed,
288                    event: FrameEvent::NeedInput,
289                });
290            }
291            let total = self
292                .total
293                .checked_add(count as u64)
294                .filter(|&n| n <= self.limit)
295                .ok_or(Error::LimitExceeded)?;
296            self.storage[self.filled..self.filled + count]
297                .copy_from_slice(&input[consumed..consumed + count]);
298            self.filled += count;
299            consumed += count;
300            self.total = total;
301            if self.filled == 4 && self.target == 4 {
302                let length = pktline::length(&self.storage[..4])?;
303                if length == 0 {
304                    self.ready = true;
305                    return Ok(FrameProgress {
306                        consumed,
307                        event: FrameEvent::Flush,
308                    });
309                }
310                if length > self.storage.len() {
311                    return Err(Error::BufferTooSmall);
312                }
313                self.target = length;
314            }
315        }
316        self.ready = true;
317        Ok(FrameProgress {
318            consumed,
319            event: FrameEvent::Data(&self.storage[4..self.target]),
320        })
321    }
322}