Skip to content

Commit

Permalink
Add support for Echo requests (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
jgiannuzzi authored Jan 22, 2025
1 parent 52b943b commit 4fc3b7e
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 0 deletions.
5 changes: 5 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<share>` or `\\<server>\<share>`.
// Note that the mounted share doesn't inherit session's context.
Expand Down
42 changes: 42 additions & 0 deletions internal/smb2/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
//
Expand Down
42 changes: 42 additions & 0 deletions internal/smb2/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
//
Expand Down
18 changes: 18 additions & 0 deletions session.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
8 changes: 8 additions & 0 deletions smb2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}

0 comments on commit 4fc3b7e

Please sign in to comment.