Initial
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
target/
|
||||||
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "messages"
|
||||||
|
version = "0.1.0"
|
||||||
2
README.md
Normal file
2
README.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# sans-gres
|
||||||
|
A sans-io based implementation of the postgres protocol
|
||||||
3
cargo.toml
Normal file
3
cargo.toml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[workspace]
|
||||||
|
members = ["messages"]
|
||||||
|
resolver = "3"
|
||||||
1
messages/.gitignore
vendored
Normal file
1
messages/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
6
messages/Cargo.toml
Normal file
6
messages/Cargo.toml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[package]
|
||||||
|
name = "messages"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
50
messages/src/datatypes.rs
Normal file
50
messages/src/datatypes.rs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
//! [Postgres Docs](https://www.postgresql.org/docs/current/protocol-message-types.html#PROTOCOL-MESSAGE-TYPES)
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum DataTypeError {
|
||||||
|
Other(&'static str),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait DataType: Sized {
|
||||||
|
fn parse<'b1, 'b2>(buffer: &'b1 [u8]) -> Result<(Self, &'b2 [u8]), DataTypeError> where 'b1: 'b2;
|
||||||
|
|
||||||
|
fn encoding_length(&self) -> usize;
|
||||||
|
fn encode<W>(&self, writer: &mut W) where W: std::io::Write;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataType for i32 {
|
||||||
|
fn parse<'b1, 'b2>(buffer: &'b1 [u8]) -> Result<(Self, &'b2 [u8]), DataTypeError> where 'b1: 'b2 {
|
||||||
|
let relevant_data = &buffer[0..4];
|
||||||
|
let value = i32::from_be_bytes(relevant_data.try_into().unwrap());
|
||||||
|
|
||||||
|
Ok((value, &buffer[4..]))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encoding_length(&self) -> usize {
|
||||||
|
4
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode<W>(&self, writer: &mut W) where W: std::io::Write {
|
||||||
|
let content = self.to_be_bytes();
|
||||||
|
writer.write_all(&content).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataType for String {
|
||||||
|
fn parse<'b1, 'b2>(buffer: &'b1 [u8]) -> Result<(Self, &'b2 [u8]), DataTypeError> where 'b1: 'b2 {
|
||||||
|
let str_length = buffer.iter().enumerate().find(|(_, v)| **v == 0).map(|(i, _)| i).unwrap();
|
||||||
|
let raw_content = &buffer[0..str_length];
|
||||||
|
let content = str::from_utf8(raw_content).unwrap();
|
||||||
|
|
||||||
|
Ok((content.to_owned(), &buffer[str_length+1..]))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encoding_length(&self) -> usize {
|
||||||
|
self.len() + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode<W>(&self, writer: &mut W) where W: std::io::Write {
|
||||||
|
writer.write_all(self.as_bytes()).unwrap();
|
||||||
|
writer.write_all(&[0]).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
31
messages/src/lib.rs
Normal file
31
messages/src/lib.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
pub mod startup;
|
||||||
|
pub mod datatypes;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum MessageParseError {
|
||||||
|
MismatchedId {
|
||||||
|
expected: u8,
|
||||||
|
found: u8,
|
||||||
|
},
|
||||||
|
Other(&'static str),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum EncodeError {
|
||||||
|
IO(std::io::Error),
|
||||||
|
Other(&'static str),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Message: Sized {
|
||||||
|
const ID: Option<u8>;
|
||||||
|
|
||||||
|
fn parse<'b1, 'b2>(buffer: &'b1 [u8]) -> Result<(Self, &'b2 [u8]), MessageParseError> where 'b1: 'b2;
|
||||||
|
|
||||||
|
fn encode<W>(&self, writer: &mut W) -> Result<(), EncodeError> where W: std::io::Write;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for EncodeError {
|
||||||
|
fn from(value: std::io::Error) -> Self {
|
||||||
|
Self::IO(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
229
messages/src/startup.rs
Normal file
229
messages/src/startup.rs
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
use crate::{EncodeError, Message, MessageParseError, datatypes::DataType};
|
||||||
|
|
||||||
|
/// Sent by the Frontend
|
||||||
|
///
|
||||||
|
/// [Postgres Docs](https://www.postgresql.org/docs/current/protocol-message-formats.html#PROTOCOL-MESSAGE-FORMATS-SSLREQUEST)
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct SSLRequest {}
|
||||||
|
|
||||||
|
impl Message for SSLRequest {
|
||||||
|
const ID: Option<u8> = None;
|
||||||
|
|
||||||
|
fn parse<'b1, 'b2>(buffer: &'b1 [u8]) -> Result<(Self, &'b2 [u8]), MessageParseError> where 'b1: 'b2 {
|
||||||
|
let (length, _) = i32::parse(buffer).unwrap();
|
||||||
|
let (code, _) = i32::parse(&buffer[4..8]).unwrap();
|
||||||
|
|
||||||
|
if length != 8 || code != 80877103 {
|
||||||
|
return Err(MessageParseError::Other("Not an SSLRequest"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((Self {}, &buffer[8..]))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode<W>(&self, writer: &mut W) -> Result<(), EncodeError> where W: std::io::Write {
|
||||||
|
8i32.encode(writer);
|
||||||
|
80877103i32.encode(writer);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sent by the Backend
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct SSLResponse {
|
||||||
|
pub enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message for SSLResponse {
|
||||||
|
const ID: Option<u8> = None;
|
||||||
|
|
||||||
|
fn parse<'b1, 'b2>(buffer: &'b1 [u8]) -> Result<(Self, &'b2 [u8]), MessageParseError> where 'b1: 'b2 {
|
||||||
|
let flag = buffer[0];
|
||||||
|
|
||||||
|
if flag != b'S' && flag != b'N' {
|
||||||
|
return Err(MessageParseError::Other("Invalid value for SSL Response"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((Self {
|
||||||
|
enabled: flag == b'S',
|
||||||
|
}, &buffer[1..]))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode<W>(&self, writer: &mut W) -> Result<(), EncodeError> where W: std::io::Write {
|
||||||
|
let flag = if self.enabled { b'S' } else { b'N' };
|
||||||
|
writer.write_all(&[flag])?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sent by the frontend
|
||||||
|
///
|
||||||
|
/// [Postgres Docs](https://www.postgresql.org/docs/current/protocol-message-formats.html#PROTOCOL-MESSAGE-FORMATS-STARTUPMESSAGE)
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct Startup {
|
||||||
|
pub version: i32,
|
||||||
|
pub parameters: Vec<(String, String)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message for Startup {
|
||||||
|
const ID: Option<u8> = None;
|
||||||
|
|
||||||
|
fn parse<'b1, 'b2>(in_buffer: &'b1 [u8]) -> Result<(Self, &'b2 [u8]), crate::MessageParseError> where 'b1: 'b2 {
|
||||||
|
let (length, _) = i32::parse(in_buffer).unwrap();
|
||||||
|
|
||||||
|
let buffer = &in_buffer[4..length as usize];
|
||||||
|
|
||||||
|
let (version, buffer) = i32::parse(buffer).unwrap();
|
||||||
|
|
||||||
|
let mut parameters = Vec::new();
|
||||||
|
|
||||||
|
let mut buffer = buffer;
|
||||||
|
while buffer.first().is_some_and(|v| *v != 0) {
|
||||||
|
let (name, tmp_buffer) = String::parse(buffer).unwrap();
|
||||||
|
let (value, tmp_buffer) = String::parse(tmp_buffer).unwrap();
|
||||||
|
|
||||||
|
parameters.push((name, value));
|
||||||
|
|
||||||
|
buffer = tmp_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !buffer.first().is_some_and(|v| *v == 0) {
|
||||||
|
return Err(MessageParseError::Other("TODO"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((Self {
|
||||||
|
version,
|
||||||
|
parameters,
|
||||||
|
}, &in_buffer[length as usize..]))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode<W>(&self, writer: &mut W) -> Result<(), EncodeError> where W: std::io::Write {
|
||||||
|
let version_length = self.version.encoding_length();
|
||||||
|
let parameters_length = self.parameters.iter().map(|(k, v)| k.encoding_length() + v.encoding_length()).sum::<usize>()+1;
|
||||||
|
|
||||||
|
((4+version_length+parameters_length) as i32).encode(writer);
|
||||||
|
self.version.encode(writer);
|
||||||
|
for (k, v) in self.parameters.iter() {
|
||||||
|
k.encode(writer);
|
||||||
|
v.encode(writer);
|
||||||
|
}
|
||||||
|
writer.write_all(&[0])?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sent by the backend
|
||||||
|
///
|
||||||
|
/// [Postgres Docs](https://www.postgresql.org/docs/current/protocol-message-formats.html#PROTOCOL-MESSAGE-FORMATS-AUTHENTICATIONOK)
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct AuthenticationOk {}
|
||||||
|
|
||||||
|
impl Message for AuthenticationOk {
|
||||||
|
const ID: Option<u8> = Some(b'R');
|
||||||
|
|
||||||
|
fn parse<'b1, 'b2>(buffer: &'b1 [u8]) -> Result<(Self, &'b2 [u8]), MessageParseError> where 'b1: 'b2 {
|
||||||
|
let id = buffer[0];
|
||||||
|
if id != Self::ID.unwrap() {
|
||||||
|
return Err(MessageParseError::MismatchedId { expected: Self::ID.unwrap(), found: id });
|
||||||
|
}
|
||||||
|
|
||||||
|
let (len, buffer) = i32::parse(&buffer[1..]).unwrap();
|
||||||
|
if len != 8 {
|
||||||
|
return Err(MessageParseError::Other("Expected a length of 8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (code, buffer) = i32::parse(buffer).unwrap();
|
||||||
|
if code != 0 {
|
||||||
|
return Err(MessageParseError::Other("Expectede the code to be 0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((Self {}, buffer))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode<W>(&self, writer: &mut W) -> Result<(), EncodeError> where W: std::io::Write {
|
||||||
|
writer.write_all(&[Self::ID.unwrap()])?;
|
||||||
|
8i32.encode(writer);
|
||||||
|
0i32.encode(writer);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sent by the backend
|
||||||
|
///
|
||||||
|
/// [Postgres Docs](https://www.postgresql.org/docs/current/protocol-message-formats.html#PROTOCOL-MESSAGE-FORMATS-PARAMETERSTATUS)
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct ParameterStatus {
|
||||||
|
pub name: String,
|
||||||
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message for ParameterStatus {
|
||||||
|
const ID: Option<u8> = Some(b'S');
|
||||||
|
|
||||||
|
fn parse<'b1, 'b2>(buffer: &'b1 [u8]) -> Result<(Self, &'b2 [u8]), MessageParseError> where 'b1: 'b2 {
|
||||||
|
let id = buffer[0];
|
||||||
|
if id != Self::ID.unwrap() {
|
||||||
|
return Err(MessageParseError::MismatchedId { expected: Self::ID.unwrap(), found: id });
|
||||||
|
}
|
||||||
|
|
||||||
|
let (len, buffer) = i32::parse(&buffer[1..]).unwrap();
|
||||||
|
|
||||||
|
let (name, buffer) = String::parse(buffer).unwrap();
|
||||||
|
let (value, buffer) = String::parse(buffer).unwrap();
|
||||||
|
|
||||||
|
Ok((Self { name, value }, buffer))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode<W>(&self, writer: &mut W) -> Result<(), EncodeError> where W: std::io::Write {
|
||||||
|
let name_length = self.name.encoding_length();
|
||||||
|
let value_length = self.value.encoding_length();
|
||||||
|
|
||||||
|
writer.write_all(&[Self::ID.unwrap()])?;
|
||||||
|
((4+name_length+value_length) as i32).encode(writer);
|
||||||
|
self.name.encode(writer);
|
||||||
|
self.value.encode(writer);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sent by the backend
|
||||||
|
///
|
||||||
|
/// [Postgres Docs](https://www.postgresql.org/docs/current/protocol-message-formats.html#PROTOCOL-MESSAGE-FORMATS-BACKENDKEYDATA)
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct BackendKeyData {
|
||||||
|
pub process_id: i32,
|
||||||
|
pub secret_key: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message for BackendKeyData {
|
||||||
|
const ID: Option<u8> = Some(b'K');
|
||||||
|
|
||||||
|
fn parse<'b1, 'b2>(buffer: &'b1 [u8]) -> Result<(Self, &'b2 [u8]), MessageParseError> where 'b1: 'b2 {
|
||||||
|
let id = buffer[0];
|
||||||
|
if id != Self::ID.unwrap() {
|
||||||
|
return Err(MessageParseError::MismatchedId { expected: Self::ID.unwrap(), found: id });
|
||||||
|
}
|
||||||
|
|
||||||
|
let (len, buffer) = i32::parse(&buffer[1..]).unwrap();
|
||||||
|
|
||||||
|
let inner_buffer = &buffer[..len as usize - 4];
|
||||||
|
|
||||||
|
let (process_id, inner_buffer) = i32::parse(inner_buffer).unwrap();
|
||||||
|
|
||||||
|
Ok((Self {
|
||||||
|
process_id,
|
||||||
|
secret_key: inner_buffer.to_vec(),
|
||||||
|
}, &buffer[len as usize - 4..]))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode<W>(&self, writer: &mut W) -> Result<(), EncodeError> where W: std::io::Write {
|
||||||
|
let process_len = self.process_id.encoding_length();
|
||||||
|
let secret_key_length = self.secret_key.len();
|
||||||
|
|
||||||
|
writer.write_all(&[Self::ID.unwrap()])?;
|
||||||
|
((4+process_len+secret_key_length) as i32).encode(writer);
|
||||||
|
self.process_id.encode(writer);
|
||||||
|
writer.write_all(self.secret_key.as_slice())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
100
messages/tests/startup.rs
Normal file
100
messages/tests/startup.rs
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
use messages::{Message, startup::{AuthenticationOk, BackendKeyData, ParameterStatus, SSLRequest, SSLResponse, Startup}};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn collected_startup() {
|
||||||
|
let raw_bytes: &[u8] = &[0x0, 0x0, 0x0, 0x3f, 0x0, 0x3, 0x0, 0x0, 0x75, 0x73, 0x65, 0x72, 0x0, 0x70, 0x6f, 0x73, 0x74, 0x67, 0x72, 0x65, 0x73, 0x0, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x0, 0x70, 0x6f, 0x73, 0x74, 0x67, 0x72, 0x65, 0x73, 0x0, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x0, 0x70, 0x73, 0x71, 0x6c, 0x0, 0x0];
|
||||||
|
|
||||||
|
let (parsed, rem) = Startup::parse(raw_bytes).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(&[] as &[u8], rem);
|
||||||
|
assert_eq!(Startup {
|
||||||
|
version: 196608,
|
||||||
|
parameters: vec![
|
||||||
|
("user".into(), "postgres".into()),
|
||||||
|
("database".into(), "postgres".into()),
|
||||||
|
("application_name".into(), "psql".into()),
|
||||||
|
],
|
||||||
|
}, parsed);
|
||||||
|
|
||||||
|
let mut output = Vec::<u8>::new();
|
||||||
|
parsed.encode(&mut output);
|
||||||
|
assert_eq!(raw_bytes, output.as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn collected_sslrequest() {
|
||||||
|
let raw_bytes: &[u8] = &[0x0, 0x0, 0x0, 0x8, 0x4, 0xd2, 0x16, 0x2f];
|
||||||
|
|
||||||
|
let (parsed, rem) = SSLRequest::parse(raw_bytes).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(&[] as &[u8], rem);
|
||||||
|
assert_eq!(SSLRequest {}, parsed);
|
||||||
|
|
||||||
|
let mut output = Vec::<u8>::new();
|
||||||
|
parsed.encode(&mut output);
|
||||||
|
assert_eq!(raw_bytes, output.as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn collected_sslresponse() {
|
||||||
|
let raw_bytes: &[u8] = &[0x4e];
|
||||||
|
|
||||||
|
let (parsed, rem) = SSLResponse::parse(raw_bytes).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(&[] as &[u8], rem);
|
||||||
|
assert_eq!(SSLResponse {
|
||||||
|
enabled: false,
|
||||||
|
}, parsed);
|
||||||
|
|
||||||
|
let mut output = Vec::<u8>::new();
|
||||||
|
parsed.encode(&mut output);
|
||||||
|
assert_eq!(raw_bytes, output.as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn collected_authenticationok() {
|
||||||
|
let raw_bytes: &[u8] = &[0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x0];
|
||||||
|
|
||||||
|
let (parsed, rem) = AuthenticationOk::parse(raw_bytes).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(&[] as &[u8], rem);
|
||||||
|
assert_eq!(AuthenticationOk {}, parsed);
|
||||||
|
|
||||||
|
let mut output = Vec::<u8>::new();
|
||||||
|
parsed.encode(&mut output);
|
||||||
|
assert_eq!(raw_bytes, output.as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn collected_parameterstatus() {
|
||||||
|
let raw_bytes: &[u8] = &[0x53, 0x0, 0x0, 0x0, 0x17, 0x69, 0x6e, 0x5f, 0x68, 0x6f, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x62, 0x79, 0x0, 0x6f, 0x66, 0x66, 0x0];
|
||||||
|
|
||||||
|
let (parsed, rem) = ParameterStatus::parse(raw_bytes).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(&[] as &[u8], rem);
|
||||||
|
assert_eq!(ParameterStatus {
|
||||||
|
name: "in_hot_standby".into(),
|
||||||
|
value: "off".into(),
|
||||||
|
}, parsed);
|
||||||
|
|
||||||
|
let mut output = Vec::<u8>::new();
|
||||||
|
parsed.encode(&mut output);
|
||||||
|
assert_eq!(raw_bytes, output.as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn collected_backendkeydata() {
|
||||||
|
let raw_bytes: &[u8] = &[0x4b, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x4b, 0x72, 0xb, 0xd3, 0xd6];
|
||||||
|
|
||||||
|
let (parsed, rem) = BackendKeyData::parse(raw_bytes).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(&[] as &[u8], rem);
|
||||||
|
assert_eq!(BackendKeyData {
|
||||||
|
process_id: 75,
|
||||||
|
secret_key: vec![0x72, 0x0b, 0xd3, 0xd6],
|
||||||
|
}, parsed);
|
||||||
|
|
||||||
|
let mut output = Vec::<u8>::new();
|
||||||
|
parsed.encode(&mut output);
|
||||||
|
assert_eq!(raw_bytes, output.as_slice());
|
||||||
|
}
|
||||||
1
traces/README.md
Normal file
1
traces/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# Traces
|
||||||
BIN
traces/output/container.pcap
Normal file
BIN
traces/output/container.pcap
Normal file
Binary file not shown.
27
traces/trace.sh
Normal file
27
traces/trace.sh
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -xe
|
||||||
|
|
||||||
|
DB_CONTAINER_ID=$(docker run -d -e POSTGRES_PASSWORD=test postgres:18)
|
||||||
|
|
||||||
|
echo $DB_CONTAINER_ID
|
||||||
|
|
||||||
|
mkdir -p output/
|
||||||
|
|
||||||
|
TRACE_CONTAINER_ID=$(docker run --rm \
|
||||||
|
--network container:$DB_CONTAINER_ID \
|
||||||
|
--cap-add NET_RAW \
|
||||||
|
--cap-add NET_ADMIN \
|
||||||
|
-v "$PWD/output:/captures" \
|
||||||
|
-d \
|
||||||
|
nicolaka/netshoot \
|
||||||
|
tcpdump -i lo -nn -U -w /captures/container.pcap)
|
||||||
|
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
echo "SELECT 1;" | docker run --rm --network container:$DB_CONTAINER_ID -i postgres:18 psql -h localhost -p 5432 -U postgres;
|
||||||
|
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
docker stop $TRACE_CONTAINER_ID
|
||||||
|
docker stop $DB_CONTAINER_ID
|
||||||
Reference in New Issue
Block a user