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 if s.chars()
94 .all(|c| !c.is_control() || c == '\n' || c == '\r' || c == '\t')
95 {
96 return write!(f, "\"{}\"", s);
97 }
98 }
99 write!(f, "0x")?;
101 for byte in b {
102 write!(f, "{:02x}", byte)?;
103 }
104 Ok(())
105 }
106 ProtoValue::Nested(fields) => {
107 write!(f, "{{ ")?;
108 for (i, field) in fields.iter().enumerate() {
109 if i > 0 {
110 write!(f, ", ")?;
111 }
112 write!(f, "{}", field)?;
113 }
114 write!(f, " }}")
115 }
116 }
117 }
118}
119
120impl fmt::Display for ProtoField {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 write!(
123 f,
124 "field {} ({}): {}",
125 self.field_number, self.wire_type, self.value
126 )
127 }
128}
129
130fn decode_varint(buf: &[u8]) -> Option<(u64, usize)> {
132 let mut value: u64 = 0;
133 let mut shift = 0;
134 for (i, &byte) in buf.iter().enumerate() {
135 if shift >= 64 {
136 return None;
137 }
138 value |= ((byte & 0x7F) as u64) << shift;
139 shift += 7;
140 if byte & 0x80 == 0 {
141 return Some((value, i + 1));
142 }
143 }
144 None
145}
146
147pub fn decode_protobuf(data: &[u8]) -> Option<Vec<ProtoField>> {
151 let mut fields = Vec::new();
152 let mut pos = 0;
153
154 while pos < data.len() {
155 let (tag, tag_len) = decode_varint(&data[pos..])?;
157 pos += tag_len;
158
159 let wire_type_raw = (tag & 0x07) as u32;
160 let field_number = (tag >> 3) as u32;
161
162 if field_number == 0 {
164 return None;
165 }
166
167 let wire_type = WireType::from(wire_type_raw);
168
169 let value = match wire_type {
170 WireType::Varint => {
171 let (v, v_len) = decode_varint(&data[pos..])?;
172 pos += v_len;
173 ProtoValue::Varint(v)
174 }
175 WireType::Bit64 => {
176 if pos + 8 > data.len() {
177 return None;
178 }
179 let mut buf = &data[pos..pos + 8];
180 let v = buf.get_u64_le();
181 pos += 8;
182 ProtoValue::Fixed64(v)
183 }
184 WireType::LengthDelimited => {
185 let (len, len_bytes) = decode_varint(&data[pos..])?;
186 pos += len_bytes;
187 let len = len as usize;
188 if pos + len > data.len() {
189 return None;
190 }
191 let payload = &data[pos..pos + len];
192 pos += len;
193
194 if let Some(nested) = decode_protobuf(payload) {
196 if !nested.is_empty() {
197 ProtoValue::Nested(nested)
198 } else {
199 ProtoValue::Bytes(payload.to_vec())
200 }
201 } else {
202 ProtoValue::Bytes(payload.to_vec())
203 }
204 }
205 WireType::Bit32 => {
206 if pos + 4 > data.len() {
207 return None;
208 }
209 let mut buf = &data[pos..pos + 4];
210 let v = buf.get_u32_le();
211 pos += 4;
212 ProtoValue::Fixed32(v)
213 }
214 WireType::StartGroup | WireType::EndGroup => {
215 return None;
217 }
218 WireType::Unknown(_) => {
219 return None;
220 }
221 };
222
223 fields.push(ProtoField {
224 field_number,
225 wire_type,
226 value,
227 });
228 }
229
230 Some(fields)
231}
232
233#[derive(Debug)]
235pub struct GrpcFrame {
236 pub compressed: bool,
238 pub payload: Vec<u8>,
240 pub decoded_fields: Option<Vec<ProtoField>>,
242}
243
244impl fmt::Display for GrpcFrame {
245 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
246 write!(
247 f,
248 "gRPC frame (compressed={}, {} bytes)",
249 self.compressed,
250 self.payload.len()
251 )?;
252 if let Some(ref fields) = self.decoded_fields {
253 for field in fields {
254 write!(f, "\n {}", field)?;
255 }
256 } else {
257 write!(f, "\n <raw bytes: {} bytes>", self.payload.len())?;
258 }
259 Ok(())
260 }
261}
262
263pub fn parse_grpc_frames(data: &[u8]) -> Vec<GrpcFrame> {
270 let mut frames = Vec::new();
271 let mut pos = 0;
272
273 while pos + 5 <= data.len() {
274 let compressed = data[pos] != 0;
275 pos += 1;
276
277 let length =
278 u32::from_be_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]) as usize;
279 pos += 4;
280
281 if pos + length > data.len() {
282 break;
283 }
284
285 let payload = data[pos..pos + length].to_vec();
286 pos += length;
287
288 let decoded_fields = if !compressed {
289 decode_protobuf(&payload)
290 } else {
291 None
292 };
293
294 frames.push(GrpcFrame {
295 compressed,
296 payload,
297 decoded_fields,
298 });
299 }
300
301 frames
302}
303
304pub fn hex_dump(data: &[u8], max_bytes: usize) -> String {
306 let truncated = data.len() > max_bytes;
307 let display = &data[..data.len().min(max_bytes)];
308
309 let mut result = String::new();
310 for (i, chunk) in display.chunks(16).enumerate() {
311 if i > 0 {
312 result.push('\n');
313 }
314 result.push_str(&format!(" {:04x}: ", i * 16));
315 for (j, byte) in chunk.iter().enumerate() {
316 if j == 8 {
317 result.push(' ');
318 }
319 result.push_str(&format!("{:02x} ", byte));
320 }
321 let remaining = 16 - chunk.len();
323 for j in 0..remaining {
324 if chunk.len() + j == 8 {
325 result.push(' ');
326 }
327 result.push_str(" ");
328 }
329 result.push_str(" |");
330 for byte in chunk {
331 if byte.is_ascii_graphic() || *byte == b' ' {
332 result.push(*byte as char);
333 } else {
334 result.push('.');
335 }
336 }
337 result.push('|');
338 }
339
340 if truncated {
341 result.push_str(&format!(
342 "\n ... ({} bytes truncated)",
343 data.len() - max_bytes
344 ));
345 }
346
347 result
348}
349
350pub fn format_grpc_message(data: &[u8]) -> String {
352 let frames = parse_grpc_frames(data);
353 if frames.is_empty() {
354 return format!(" <no valid gRPC frames, {} raw bytes>", data.len());
355 }
356
357 let mut result = String::new();
358 for (i, frame) in frames.iter().enumerate() {
359 if i > 0 {
360 result.push('\n');
361 }
362 result.push_str(&format!("{}", frame));
363 }
364 result
365}
366
367#[cfg(test)]
368mod tests {
369 use super::*;
370
371 #[test]
372 fn test_decode_varint() {
373 assert_eq!(decode_varint(&[0x01]), Some((1, 1)));
375 assert_eq!(decode_varint(&[0x7F]), Some((127, 1)));
377 assert_eq!(decode_varint(&[0x80, 0x01]), Some((128, 2)));
379 assert_eq!(decode_varint(&[0xAC, 0x02]), Some((300, 2)));
381 }
382
383 #[test]
384 fn test_decode_simple_protobuf() {
385 let data = vec![0x08, 0x96, 0x01];
387 let fields = decode_protobuf(&data).unwrap();
388 assert_eq!(fields.len(), 1);
389 assert_eq!(fields[0].field_number, 1);
390 assert_eq!(fields[0].wire_type, WireType::Varint);
391 match &fields[0].value {
392 ProtoValue::Varint(v) => assert_eq!(*v, 150),
393 _ => panic!("Expected varint"),
394 }
395 }
396
397 #[test]
398 fn test_decode_string_field() {
399 let data = vec![0x12, 0x07, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67];
401 let fields = decode_protobuf(&data).unwrap();
402 assert_eq!(fields.len(), 1);
403 assert_eq!(fields[0].field_number, 2);
404 assert_eq!(fields[0].wire_type, WireType::LengthDelimited);
405 match &fields[0].value {
406 ProtoValue::Bytes(b) => assert_eq!(b, b"testing"),
407 _ => panic!("Expected bytes"),
408 }
409 }
410
411 #[test]
412 fn test_parse_grpc_frame() {
413 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);
419 assert_eq!(frames.len(), 1);
420 assert!(!frames[0].compressed);
421 assert!(frames[0].decoded_fields.is_some());
422 let fields = frames[0].decoded_fields.as_ref().unwrap();
423 assert_eq!(fields.len(), 1);
424 assert_eq!(fields[0].field_number, 1);
425 }
426
427 #[test]
428 fn test_invalid_protobuf() {
429 let data = vec![
431 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
432 ];
433 assert!(decode_protobuf(&data).is_none());
434 }
435
436 #[test]
437 fn test_hex_dump() {
438 let data = b"Hello, World!";
439 let dump = hex_dump(data, 256);
440 assert!(dump.contains("48 65 6c 6c"));
441 assert!(dump.contains("|Hello, World!|"));
442 }
443
444 #[test]
445 fn test_empty_data() {
446 let fields = decode_protobuf(&[]);
447 assert_eq!(fields, Some(vec![]));
448 }
449
450 #[test]
451 fn test_wire_type_display() {
452 assert_eq!(format!("{}", WireType::Varint), "varint");
453 assert_eq!(format!("{}", WireType::LengthDelimited), "length-delimited");
454 assert_eq!(format!("{}", WireType::Unknown(99)), "unknown(99)");
455 }
456}