netshovel

Network Archaeology library for Go
git clone https://git.woozle.org/neale/netshovel.git

Neale Pickett  ·  2020-09-24

hk_test.go

  1// hk_test is a start at a decoder I was writing, which exhibited some problems.
  2// It also illustrates what a real decoder might look like.
  3
  4package netshovel
  5
  6import (
  7	"fmt"
  8	"io"
  9	"log"
 10	"strings"
 11	"sync"
 12	"testing"
 13
 14	"github.com/google/gopacket"
 15	"github.com/google/gopacket/tcpassembly"
 16)
 17
 18var wg sync.WaitGroup
 19
 20// HKStreamFactory generates HKStreams.
 21type HKStreamFactory struct {
 22	err *error
 23}
 24
 25// New returns a new HKStream.
 26func (f *HKStreamFactory) New(net, transport gopacket.Flow) tcpassembly.Stream {
 27	stream := &HKStream{
 28		Stream: NewStream(net, transport),
 29		err:    f.err,
 30	}
 31	wg.Add(1)
 32	go stream.Decode(&wg)
 33
 34	return stream
 35}
 36
 37// HKStream represents half of a TCP Stream.
 38type HKStream struct {
 39	*Stream
 40	err *error
 41}
 42
 43func (stream HKStream) Read(length int) (Utterance, error) {
 44	u, err := stream.Stream.Read(length)
 45	return u, err
 46}
 47
 48// DisplayUtterance prints an unparsed TCP utterance
 49func (stream HKStream) DisplayUtterance(u Utterance) {
 50	fmt.Printf("Unparsed %v:%v → %v:%v\n",
 51		stream.Net.Src().String(), stream.Transport.Src().String(),
 52		stream.Net.Dst().String(), stream.Transport.Dst().String(),
 53	)
 54	fmt.Println(u.Data.Hexdump())
 55}
 56
 57// Display prints as much about an HKPacket as we are able to determine.
 58func (stream HKStream) Display(pkt HKPacket) {
 59	out := new(strings.Builder)
 60
 61	fmt.Fprintf(out, "HK %v:%v → %v:%v\n",
 62		stream.Net.Src().String(), stream.Transport.Src().String(),
 63		stream.Net.Dst().String(), stream.Transport.Dst().String(),
 64	)
 65	out.WriteString(pkt.Describe())
 66	fmt.Println(out.String())
 67}
 68
 69// Decode decodes all data from the stream.
 70func (stream HKStream) Decode(wg *sync.WaitGroup) {
 71	defer wg.Done()
 72	for {
 73		utterance, err := stream.Read(2)
 74		if err == io.EOF {
 75			return
 76		} else if err != nil {
 77			log.Println(err)
 78			return
 79		}
 80
 81		// Was it actually HK?
 82		if utterance.Data.String("DROP") != "HK" {
 83			u, err := stream.Read(-1)
 84			if err != nil {
 85				log.Println(err)
 86				return
 87			}
 88
 89			if utterance.When != u.When {
 90				stream.DisplayUtterance(utterance)
 91				utterance = u
 92				*stream.err = fmt.Errorf("Short length on non-HK packet, and a different utterance was returned")
 93			} else {
 94				utterance.Data = utterance.Data.Append(u.Data)
 95			}
 96			if utterance.Data.Length() < 10 {
 97				*stream.err = fmt.Errorf("Short length on non-HK packet")
 98				return
 99			}
100			stream.DisplayUtterance(utterance)
101			continue
102		}
103
104		pkt := NewHKPacket(utterance)
105		if err := pkt.Decode(stream); err != nil {
106			log.Println(err)
107			return
108		}
109		stream.Display(pkt)
110	}
111}
112
113// NewHKPacket returns a shiny new HKPacket.
114func NewHKPacket(u Utterance) HKPacket {
115	pkt := HKPacket{
116		Packet: NewPacket(),
117	}
118	pkt.Payload = u.Data
119	pkt.When = u.When
120	return pkt
121}
122
123// HKPacket is a single HK packet.
124type HKPacket struct {
125	Packet
126}
127
128// Decode from a readable
129func (pkt *HKPacket) Decode(stream HKStream) error {
130	header, err := stream.Read(9)
131	if err != nil {
132		return err
133	}
134	pkt.Payload = header.Data
135
136	unknown, _ := pkt.Uint32BE("unknown")
137	length, _ := pkt.Uint32BE("length")
138	opcode, _ := pkt.Uint8("opcode")
139	pkt.Opcode = int(opcode)
140
141	if unknown != 0 {
142		return fmt.Errorf("unknown header was actually %d", unknown)
143	}
144	if length > 100 {
145		return fmt.Errorf("Length too big: %d", length)
146	}
147
148	body, err := stream.Read(int(length - 9 - 2))
149	if err != nil {
150		return err
151	}
152	pkt.Payload = body.Data
153
154	subcode, _ := pkt.Uint8("subcode")
155	if subcode != 0 {
156		return fmt.Errorf("Subcode not zero: %d", subcode)
157	}
158	pkt.SetString("Payload", pkt.Payload.String("DROP"))
159
160	switch pkt.Opcode {
161	case 7:
162		pkt.Description = "Keepalive"
163	}
164
165	return nil
166}
167
168func TestHK(t *testing.T) {
169	factory := HKStreamFactory{err: new(error)}
170	streamPool := tcpassembly.NewStreamPool(&factory)
171	assembler := tcpassembly.NewAssembler(streamPool)
172	ShovelFile("testdata/hk.pcap", assembler)
173	assembler.FlushAll()
174	wg.Wait()
175
176	if *factory.err != nil {
177		t.Error(*factory.err)
178	}
179}