From 5e0cc956a3c422ab30f4cd3d02f8ee81ad4f4034 Mon Sep 17 00:00:00 2001 From: Jonathan Giannuzzi Date: Fri, 17 Jan 2025 13:54:02 +0000 Subject: [PATCH 1/2] Add support for Echo requests --- client.go | 5 +++++ internal/smb2/request.go | 42 +++++++++++++++++++++++++++++++++++++++ internal/smb2/response.go | 42 +++++++++++++++++++++++++++++++++++++++ session.go | 18 +++++++++++++++++ 4 files changed, 107 insertions(+) diff --git a/client.go b/client.go index 863edde..05fde0c 100644 --- a/client.go +++ b/client.go @@ -135,6 +135,11 @@ func (c *Session) Logoff() error { return c.s.logoff(c.ctx) } +// Echo sends an echo request to the server. +func (c *Session) Echo() error { + return c.s.echo(c.ctx) +} + // Mount mounts the SMB share. // sharename must follow format like `` or `\\\`. // Note that the mounted share doesn't inherit session's context. diff --git a/internal/smb2/request.go b/internal/smb2/request.go index 943ba06..815559f 100644 --- a/internal/smb2/request.go +++ b/internal/smb2/request.go @@ -291,6 +291,48 @@ func (r LogoffRequestDecoder) StructureSize() uint16 { return le.Uint16(r[:2]) } +// ---------------------------------------------------------------------------- +// SMB2 ECHO Request Packet +// + +type EchoRequest struct { + PacketHeader +} + +func (c *EchoRequest) Header() *PacketHeader { + return &c.PacketHeader +} + +func (c *EchoRequest) Size() int { + return 64 + 4 +} + +func (c *EchoRequest) Encode(pkt []byte) { + c.Command = SMB2_ECHO + c.encodeHeader(pkt) + + req := pkt[64:] + le.PutUint16(req[:2], 4) // StructureSize +} + +type EchoRequestDecoder []byte + +func (r EchoRequestDecoder) IsInvalid() bool { + if len(r) < 4 { + return true + } + + if r.StructureSize() != 4 { + return true + } + + return false +} + +func (r EchoRequestDecoder) StructureSize() uint16 { + return le.Uint16(r[:2]) +} + // ---------------------------------------------------------------------------- // SMB2 TREE_CONNECT Request Packet // diff --git a/internal/smb2/response.go b/internal/smb2/response.go index faf0a3c..e4dc876 100644 --- a/internal/smb2/response.go +++ b/internal/smb2/response.go @@ -631,6 +631,48 @@ func (r LogoffResponseDecoder) StructureSize() uint16 { return le.Uint16(r[:2]) } +// ---------------------------------------------------------------------------- +// SMB2 ECHO Response +// + +type EchoResponse struct { + PacketHeader +} + +func (c *EchoResponse) Header() *PacketHeader { + return &c.PacketHeader +} + +func (c *EchoResponse) Size() int { + return 64 + 4 +} + +func (c *EchoResponse) Encode(pkt []byte) { + c.Command = SMB2_ECHO + c.encodeHeader(pkt) + + res := pkt[64:] + le.PutUint16(res[:2], 4) // StructureSize +} + +type EchoResponseDecoder []byte + +func (r EchoResponseDecoder) IsInvalid() bool { + if len(r) < 4 { + return true + } + + if r.StructureSize() != 4 { + return true + } + + return false +} + +func (r EchoResponseDecoder) StructureSize() uint16 { + return le.Uint16(r[:2]) +} + // ---------------------------------------------------------------------------- // SMB2 TREE_CONNECT Response // diff --git a/session.go b/session.go index ecf4630..3f184f6 100644 --- a/session.go +++ b/session.go @@ -291,6 +291,24 @@ func (s *session) logoff(ctx context.Context) error { return nil } +func (s *session) echo(ctx context.Context) error { + req := new(EchoRequest) + + req.CreditCharge = 1 + + res, err := s.sendRecv(SMB2_ECHO, req, ctx) + if err != nil { + return err + } + + r := EchoResponseDecoder(res) + if r.IsInvalid() { + return &InvalidResponseError{"broken echo response format"} + } + + return nil +} + func (s *session) sendRecv(cmd uint16, req Packet, ctx context.Context) (res []byte, err error) { rr, err := s.send(req, ctx) if err != nil { From f7a8a82f20b6ff8bfe8c072d6cb51b429691165c Mon Sep 17 00:00:00 2001 From: Jonathan Giannuzzi Date: Wed, 22 Jan 2025 10:52:20 +0000 Subject: [PATCH 2/2] Add test for Echo --- smb2_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/smb2_test.go b/smb2_test.go index 5517f7b..cb77ae8 100644 --- a/smb2_test.go +++ b/smb2_test.go @@ -900,3 +900,11 @@ func TestGlob(t *testing.T) { t.Errorf("unexpected matches: %v != %v", matches5, expected5) } } + +func TestEcho(t *testing.T) { + if session == nil { + t.Skip() + } + + require.NoError(t, session.Echo()) +}