1use bytes::Buf;
9use std::fmt;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum WireType {
14 Varint,
16 Bit64,
18 LengthDelimited,
20 StartGroup,
22 EndGroup,
24 Bit32,
26 Unknown(u32),
28}
29
30impl fmt::Display for WireType {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 match self {
33 WireType::Varint => write!(f, "varint"),
34 WireType::Bit64 => write!(f, "64-bit"),
35 WireType::LengthDelimited => write!(f, "length-delimited"),
36 WireType::StartGroup => write!(f, "start-group"),
37 WireType::EndGroup => write!(f, "end-group"),
38 WireType::Bit32 => write!(f, "32-bit"),
39 WireType::Unknown(v) => write!(f, "unknown({})", v),
40 }
41 }
42}
43
44impl From<u32> for WireType {
45 fn from(value: u32) -> Self {
46 match value {
47 0 => WireType::Varint,
48 1 => WireType::Bit64,
49 2 => WireType::LengthDelimited,
50 3 => WireType::StartGroup,
51 4 => WireType::EndGroup,
52 5 => WireType::Bit32,
53 v => WireType::Unknown(v),
54 }
55 }
56}
57
58#[derive(Debug, Clone, PartialEq)]
60pub struct ProtoField {
61 pub field_number: u32,
63 pub wire_type: WireType,
65 pub value: ProtoValue,
67}
68
69#[derive(Debug, Clone, PartialEq)]
71pub enum ProtoValue {
72 Varint(u64),
74 Fixed32(u32),
76 Fixed64(u64),
78 Bytes(Vec<u8>),
80 Nested(Vec<ProtoField>),
82}
83
84impl fmt::Display for ProtoValue {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 match self {
87 ProtoValue::Varint(v) => write!(f, "{}", v),
88 ProtoValue::Fixed32(v) => write!(f, "0x{:08x}", v),
89 ProtoValue::Fixed64(v) => write!(f, "0x{:016x}", v),
90 ProtoValue::Bytes(b) => {
91 if let Ok(s) = std::str::from_utf8(b)
93 && s.chars()
94 .all(|c| !c.is_control() || c == '\n' || c == '\r' || c == '\t')
95 {
96 return write!(f, "\"{}\"", s);
97 }
98 write!(f, "0x")?;
100 for byte in b {
101 write!(f, "{:02x}", byte)?;
102 }
103 Ok(())
104 }
105 ProtoValue::Nested(fields) => {
106 write!(f, "{{ ")?;
107 for (i, field) in fields.iter().enumerate() {
108 if i > 0 {
109 write!(f, ", ")?;
110 }
111 write!(f, "{}", field)?;
112 }
113 write!(f, " }}")
114 }
115 }
116 }
117}
118
119impl fmt::Display for ProtoField {
120 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121 write!(
122 f,
123 "field {} ({}): {}",
124 self.field_number, self.wire_type, self.value
125 )
126 }
127}
128
129fn decode_varint(buf: &[u8]) -> Option<(u64, usize)> {
131 let mut value: u64 = 0;
132 let mut shift = 0;
133 for (i, &byte) in buf.iter().enumerate() {
134 if shift >= 64 {
135 return None;
136 }
137 value |= ((byte & 0x7F) as u64) << shift;
138 shift += 7;
139 if byte & 0x80 == 0 {
140 return Some((value, i + 1));
141 }
142 }
143 None
144}
145
146pub fn decode_protobuf(data: &[u8]) -> Option<Vec<ProtoField>> {
150 let mut fields = Vec::new();
151 let mut pos = 0;
152
153 while pos < data.len() {
154 let (tag, tag_len) = decode_varint(&data[pos..])?;
156 pos += tag_len;
157
158 let wire_type_raw = (tag & 0x07) as u32;
159 let field_number = (tag >> 3) as u32;
160
161 if field_number == 0 {
163 return None;
164 }
165
166 let wire_type = WireType::from(wire_type_raw);
167
168 let value = match wire_type {
169 WireType::Varint => {
170 let (v, v_len) = decode_varint(&data[pos..])?;
171 pos += v_len;
172 ProtoValue::Varint(v)
173 }
174 WireType::Bit64 => {
175 if pos + 8 > data.len() {
176 return None;
177 }
178 let mut buf = &data[pos..pos + 8];
179 let v = buf.get_u64_le();
180 pos += 8;
181 ProtoValue::Fixed64(v)
182 }
183 WireType::LengthDelimited => {
184 let (len, len_bytes) = decode_varint(&data[pos..])?;
185 pos += len_bytes;
186 let len = len as usize;
187 if pos + len > data.len() {
188 return None;
189 }
190 let payload = &data[pos..pos + len];
191 pos += len;
192
193 if let Some(nested) = decode_protobuf(payload) {
195 if !nested.is_empty() {
196 ProtoValue::Nested(nested)
197 } else {
198 ProtoValue::Bytes(payload.to_vec())
199 }
200 } else {
201 ProtoValue::Bytes(payload.to_vec())
202 }
203 }
204 WireType::Bit32 => {
205 if pos + 4 > data.len() {
206 return None;
207 }
208 let mut buf = &data[pos..pos + 4];
209 let v = buf.get_u32_le();
210 pos += 4;
211 ProtoValue::Fixed32(v)
212 }
213 WireType::StartGroup | WireType::EndGroup => {
214 return None;
216 }
217 WireType::Unknown(_) => {
218 return None;
219 }
220 };
221
222 fields.push(ProtoField {
223 field_number,
224 wire_type,
225 value,
226 });
227 }
228
229 Some(fields)
230}
231
232#[derive(Debug)]
234pub struct GrpcFrame {
235 pub compressed: bool,
237 pub payload: Vec<u8>,
239 pub decoded_fields: Option<Vec<ProtoField>>,
241}
242
243impl fmt::Display for GrpcFrame {
244 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245 write!(
246 f,
247 "gRPC frame (compressed={}, {} bytes)",
248 self.compressed,
249 self.payload.len()
250 )?;
251 if let Some(ref fields) = self.decoded_fields {
252 for field in fields {
253 write!(f, "\n {}", field)?;
254 }
255 } else {
256 write!(f, "\n <raw bytes: {} bytes>", self.payload.len())?;
257 }
258 Ok(())
259 }
260}
261
262pub fn parse_grpc_frames(data: &[u8]) -> Vec<GrpcFrame> {
269 let mut frames = Vec::new();
270 let mut pos = 0;
271
272 while pos + 5 <= data.len() {
273 let compressed = data[pos] != 0;
274 pos += 1;
275
276 let length =
277 u32::from_be_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]) as usize;
278 pos += 4;
279
280 if pos + length > data.len() {
281 break;
282 }
283
284 let payload = data[pos..pos + length].to_vec();
285 pos += length;
286
287 let decoded_fields = if !compressed {
288 decode_protobuf(&payload)
289 } else {
290 None
291 };
292
293 frames.push(GrpcFrame {
294 compressed,
295 payload,
296 decoded_fields,
297 });
298 }
299
300 frames
301}
302
303pub fn hex_dump(data: &[u8], max_bytes: usize) -> String {
305 let truncated = data.len() > max_bytes;
306 let display = &data[..data.len().min(max_bytes)];
307
308 let mut result = String::new();
309 for (i, chunk) in display.chunks(16).enumerate() {
310 if i > 0 {
311 result.push('\n');
312 }
313 result.push_str(&format!(" {:04x}: ", i * 16));
314 for (j, byte) in chunk.iter().enumerate() {
315 if j == 8 {
316 result.push(' ');
317 }
318 result.push_str(&format!("{:02x} ", byte));
319 }
320 let remaining = 16 - chunk.len();
322 for j in 0..remaining {
323 if chunk.len() + j == 8 {
324 result.push(' ');
325 }
326 result.push_str(" ");
327 }
328 result.push_str(" |");
329 for byte in chunk {
330 if byte.is_ascii_graphic() || *byte == b' ' {
331 result.push(*byte as char);
332 } else {
333 result.push('.');
334 }
335 }
336 result.push('|');
337 }
338
339 if truncated {
340 result.push_str(&format!(
341 "\n ... ({} bytes truncated)",
342 data.len() - max_bytes
343 ));
344 }
345
346 result
347}
348
349pub fn format_grpc_message(data: &[u8]) -> String {
351 let frames = parse_grpc_frames(data);
352 if frames.is_empty() {
353 return format!(" <no valid gRPC frames, {} raw bytes>", data.len());
354 }
355
356 let mut result = String::new();
357 for (i, frame) in frames.iter().enumerate() {
358 if i > 0 {
359 result.push('\n');
360 }
361 result.push_str(&format!("{}", frame));
362 }
363 result
364}
365
366#[cfg(test)]
367mod tests {
368 use super::*;
369
370 #[test]
371 fn test_decode_varint() {
372 assert_eq!(decode_varint(&[0x01]), Some((1, 1)));
374 assert_eq!(decode_varint(&[0x7F]), Some((127, 1)));
376 assert_eq!(decode_varint(&[0x80, 0x01]), Some((128, 2)));
378 assert_eq!(decode_varint(&[0xAC, 0x02]), Some((300, 2)));
380 }
381
382 #[test]
383 fn test_decode_simple_protobuf() {
384 let data = vec![0x08, 0x96, 0x01];
386 let fields = decode_protobuf(&data).unwrap();
387 assert_eq!(fields.len(), 1);
388 assert_eq!(fields[0].field_number, 1);
389 assert_eq!(fields[0].wire_type, WireType::Varint);
390 match &fields[0].value {
391 ProtoValue::Varint(v) => assert_eq!(*v, 150),
392 _ => panic!("Expected varint"),
393 }
394 }
395
396 #[test]
397 fn test_decode_string_field() {
398 let data = vec![0x12, 0x07, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67];
400 let fields = decode_protobuf(&data).unwrap();
401 assert_eq!(fields.len(), 1);
402 assert_eq!(fields[0].field_number, 2);
403 assert_eq!(fields[0].wire_type, WireType::LengthDelimited);
404 match &fields[0].value {
405 ProtoValue::Bytes(b) => assert_eq!(b, b"testing"),
406 _ => panic!("Expected bytes"),
407 }
408 }
409
410 #[test]
411 fn test_parse_grpc_frame() {
412 let mut data = vec![0x00]; data.extend_from_slice(&3u32.to_be_bytes()); data.extend_from_slice(&[0x08, 0x96, 0x01]); let frames = parse_grpc_frames(&data);
418 assert_eq!(frames.len(), 1);
419 assert!(!frames[0].compressed);
420 assert!(frames[0].decoded_fields.is_some());
421 let fields = frames[0].decoded_fields.as_ref().unwrap();
422 assert_eq!(fields.len(), 1);
423 assert_eq!(fields[0].field_number, 1);
424 }
425
426 #[test]
427 fn test_invalid_protobuf() {
428 let data = vec![
430 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
431 ];
432 assert!(decode_protobuf(&data).is_none());
433 }
434
435 #[test]
436 fn test_hex_dump() {
437 let data = b"Hello, World!";
438 let dump = hex_dump(data, 256);
439 assert!(dump.contains("48 65 6c 6c"));
440 assert!(dump.contains("|Hello, World!|"));
441 }
442
443 #[test]
444 fn test_empty_data() {
445 let fields = decode_protobuf(&[]);
446 assert_eq!(fields, Some(vec![]));
447 }
448
449 #[test]
450 fn test_wire_type_display() {
451 assert_eq!(format!("{}", WireType::Varint), "varint");
452 assert_eq!(format!("{}", WireType::LengthDelimited), "length-delimited");
453 assert_eq!(format!("{}", WireType::Unknown(99)), "unknown(99)");
454 }
455}