pcap-cli/internal/transformer/text_translator.go (150 lines of code) (raw):

// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build text package transformer import ( "context" "fmt" "io" "strings" "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/pterm/pterm" ) type ( TextPcapTranslator struct { iface *PcapIface } textPcapTranslation struct { index int // allows layers to be sorted on `String()` invocation builder *strings.Builder } textPcapTranslations struct { writer io.Writer translations map[int]*textPcapTranslation } ) var ( textPcapSerialStyle = pterm.NewStyle(pterm.FgBlack, pterm.BgRed) textPcapHeaderStyle = pterm.NewStyle(pterm.FgBlack, pterm.BgCyan, pterm.Bold) textPcapDataStyle = pterm.NewStyle(pterm.FgBlack, pterm.BgWhite, pterm.Bold) ) func init() { translators.Store(TEXT, newTEXTPcapTranslator) } func (tt *textPcapTranslation) String() string { return tt.builder.String() } func (tt *textPcapTranslations) String() string { translations := (*tt).translations printer := pterm.BulletListPrinter{ Bullet: "•", TextStyle: &pterm.ThemeDefault.BulletListTextStyle, BulletStyle: &pterm.ThemeDefault.BulletListBulletStyle, Items: make([]pterm.BulletListItem, len(translations)), } for key, value := range translations { printer.Items[key] = pterm.BulletListItem{ Level: key, // Level 0 (top level) Text: value.builder.String(), // Text to display TextStyle: pterm.NewStyle(pterm.FgWhite), // Text color BulletStyle: pterm.NewStyle(pterm.FgRed), // Bullet color } } str, err := printer.Srender() if err == nil { return pterm.DefaultBox.Sprintln(str) } return "" } func (t *TextPcapTranslator) done(_ context.Context) { // not implemented } func (t *TextPcapTranslator) next( ctx context.Context, nic *PcapIface, serial *uint64, packet *gopacket.Packet, ) fmt.Stringer { text := new(strings.Builder) metadata := (*packet).Metadata() info := metadata.CaptureInfo // text.WriteString(ctx.Value(ContextID)) text.WriteString(textPcapSerialStyle.Sprint(" ", *serial, " ")) text.WriteString(" | [iface: ") text.WriteString(textPcapDataStyle.Sprint(" ", t.iface.Index, "/", t.iface.Name, " ")) text.WriteString("] | [timestamp: ") text.WriteString(textPcapDataStyle.Sprint(" ", info.Timestamp.String(), " ")) text.WriteString("]") // `next` returns the container to be used for merging all layers return &textPcapTranslations{translations: map[int]*textPcapTranslation{0: {0, text}}} } func (t *TextPcapTranslator) asTranslation(buffer fmt.Stringer) *textPcapTranslation { return buffer.(*textPcapTranslation) } func (t *TextPcapTranslator) translateEthernetLayer(ctx context.Context, eth *layers.Ethernet) fmt.Stringer { text := new(strings.Builder) text.WriteString(textPcapHeaderStyle.Sprint(" L2 ")) text.WriteString(" | [") text.WriteString(textPcapDataStyle.Sprint(" ", eth.EthernetType.String(), " ")) text.WriteString("] | [src: ") text.WriteString(textPcapDataStyle.Sprint(" ", eth.SrcMAC.String(), " ")) text.WriteString("] | [dst: ") text.WriteString(textPcapDataStyle.Sprint(" ", eth.DstMAC.String(), " ")) text.WriteString("]") return &textPcapTranslation{1, text} } func (t *TextPcapTranslator) translateIPv4Layer(ctx context.Context, ip4 *layers.IPv4) fmt.Stringer { // [TODO]: implement IPv4 layer translation return &textPcapTranslation{2, new(strings.Builder)} } func (t *TextPcapTranslator) translateIPv6Layer(ctx context.Context, ip6 *layers.IPv6) fmt.Stringer { // [TODO]: implement IPv6 layer translation return &textPcapTranslation{2, new(strings.Builder)} } func (t *TextPcapTranslator) translateUDPLayer(ctx context.Context, udp *layers.UDP) fmt.Stringer { // [TODO]: implement UDP layer translation return &textPcapTranslation{3, new(strings.Builder)} } func (t *TextPcapTranslator) translateTCPLayer(ctx context.Context, tcp *layers.TCP) fmt.Stringer { // [TODO]: implement TCP layer translation return &textPcapTranslation{3, new(strings.Builder)} } func (t *TextPcapTranslator) translateTLSLayer(ctx context.Context, tls *layers.TLS) fmt.Stringer { // [TODO]: implement TLS layer translation return &textPcapTranslation{4, new(strings.Builder)} } func (t *TextPcapTranslator) translateDNSLayer(ctx context.Context, dns *layers.DNS) fmt.Stringer { // [TODO]: implement DNS layer translation return &textPcapTranslation{4, new(strings.Builder)} } func (t *TextPcapTranslator) merge(ctx context.Context, tgt fmt.Stringer, src fmt.Stringer) (fmt.Stringer, error) { srcTranslation := t.asTranslation(src) switch typedObj := tgt.(type) { case *textPcapTranslations: // add reference to another layer translation (*typedObj).translations[srcTranslation.index] = srcTranslation case *textPcapTranslation: // 1st `merge` invocation might not actually be a map (`textPcapTranslations`) tgt = &textPcapTranslations{ translations: map[int]*textPcapTranslation{ typedObj.index: typedObj, srcTranslation.index: srcTranslation, }, } } return tgt, nil } func (t *TextPcapTranslator) finalize(ctx context.Context, iface *PcapIface, serial *uint64, p *gopacket.Packet, connTrack bool, packet fmt.Stringer) (fmt.Stringer, error) { return packet, nil } func (t *TextPcapTranslator) write(ctx context.Context, writer io.Writer, packet *fmt.Stringer) (int, error) { translations := (*packet).(*textPcapTranslations) translations.writer = writer return io.WriteString(writer, translations.String()) } func newTEXTPcapTranslator( _ context.Context, _ bool, iface *PcapIface, _ *PcapEphemeralPorts, ) PcapTranslator { return &TextPcapTranslator{iface: iface} } // [TODO]: remove samples when all layers translations are implemented func (t *TextPcapTranslator) translate(packet *gopacket.Packet) error { p := *packet // Iterate over all layers, printing out each layer type fmt.Println("All packet layers:") for _, layer := range p.Layers() { fmt.Println("- ", layer.LayerType()) } // Check for errors if err := p.ErrorLayer(); err != nil { fmt.Println("Error decoding some part of the packet:", err) } return nil }