plc4go/tools/plc4xpcapanalyzer/internal/extractor/extractor.go (152 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 * * https://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. */ package extractor import ( "context" "fmt" "io" "net" "github.com/fatih/color" "github.com/gopacket/gopacket/layers" "github.com/k0kubun/go-ansi" "github.com/pkg/errors" "github.com/rs/zerolog/log" "github.com/schollz/progressbar/v3" "github.com/apache/plc4x-extras/plc4go/tools/plc4xpcapanalyzer/config" "github.com/apache/plc4x-extras/plc4go/tools/plc4xpcapanalyzer/internal/common" "github.com/apache/plc4x-extras/plc4go/tools/plc4xpcapanalyzer/internal/pcaphandler" ) func Extract(pcapFile, protocolType string) error { return ExtractWithOutput(context.TODO(), pcapFile, protocolType, ansi.NewAnsiStdout(), ansi.NewAnsiStderr()) } func ExtractWithOutput(ctx context.Context, pcapFile, protocolType string, stdout, stderr io.Writer) error { var printPayload = func(packetInformation common.PacketInformation, item []byte) { _, _ = fmt.Fprintf(stdout, "%x\n", item) } switch protocolType { case "bacnet": // nothing special as this is byte based case "c-bus": // c-bus is string based so we consume the string and print it clientIp := net.ParseIP(config.ExtractConfigInstance.Client) serverResponseWriter := color.New(color.FgRed) serverResponseIndicatorWriter := color.New(color.FgHiRed) clientRequestWriter := color.New(color.FgGreen) clientRequestIndicatorWriter := color.New(color.FgHiGreen) printPayload = func(packetInformation common.PacketInformation, payload []byte) { payloadString := "" suffix := "" extraInformation := "" if config.ExtractConfigInstance.Verbosity > 2 { extraInformation = fmt.Sprintf("(No.[%d])", packetInformation.PacketNumber) } if len(payload) > 0 { if properTerminated := payload[len(payload)-1] == 0x0D || payload[len(payload)-1] == 0x0A; properTerminated { suffix = "\n" } quotedPayload := fmt.Sprintf("%+q", payload) unquotedPayload := quotedPayload[1 : len(quotedPayload)-1] payloadString = unquotedPayload } if isResponse := packetInformation.DstIp.Equal(clientIp); isResponse { if config.ExtractConfigInstance.ShowDirectionalIndicators { _, _ = serverResponseIndicatorWriter.Fprintf(stderr, "%s(<--pci)", extraInformation) } _, _ = serverResponseWriter.Fprintf(stdout, "%s%s", payloadString, suffix) } else { if config.ExtractConfigInstance.ShowDirectionalIndicators { _, _ = clientRequestIndicatorWriter.Fprintf(stderr, "%s(-->pci)", extraInformation) } _, _ = clientRequestWriter.Fprintf(stdout, "%s%s", payloadString, suffix) } } } filterExpression := config.ExtractConfigInstance.Filter log.Info(). Str("pcapFile", pcapFile). Str("protocolType", protocolType). Str("filterExpression", filterExpression). Msg("Analyzing pcap file pcapFile with protocolType protocolType and filter filterExpression now") handle, numberOfPackage, timestampToIndexMap, err := pcaphandler.GetIndexedPcapHandle(pcapFile, filterExpression) if err != nil { return errors.Wrap(err, "Error getting handle") } log.Info().Int("numberOfPackage", numberOfPackage).Msg("Starting to analyze numberOfPackage packages") defer handle.Close() log.Debug().Interface("handle", handle).Int("numberOfPackage", numberOfPackage).Msg("got handle") source := pcaphandler.GetPacketSource(handle) bar := progressbar.NewOptions(numberOfPackage, progressbar.OptionSetWriter(ansi.NewAnsiStderr()), progressbar.OptionSetVisibility(!config.RootConfigInstance.HideProgressBar), progressbar.OptionEnableColorCodes(true), progressbar.OptionShowBytes(false), progressbar.OptionSetWidth(15), progressbar.OptionSetDescription("[cyan][1/3][reset] Analyzing packages..."), progressbar.OptionSetTheme(progressbar.Theme{ Saucer: "[green]=[reset]", SaucerHead: "[green]>[reset]", SaucerPadding: " ", BarStart: "[", BarEnd: "]", })) currentPackageNum := uint(0) parseFails := 0 serializeFails := 0 compareFails := 0 for packet := range source.Packets() { if errors.Is(ctx.Err(), context.Canceled) { log.Info(). Uint("currentPackageNum", currentPackageNum). Msg("Aborted after currentPackageNum packages") break } currentPackageNum++ if currentPackageNum < config.ExtractConfigInstance.StartPackageNumber { log.Debug(). Uint("currentPackageNum", currentPackageNum). Uint("startPackageNumber", config.ExtractConfigInstance.StartPackageNumber). Msg("Skipping package number currentPackageNum (till no. startPackageNumber)") continue } if currentPackageNum > config.ExtractConfigInstance.PackageNumberLimit { log.Warn(). Uint("packageNumberLimit", config.ExtractConfigInstance.PackageNumberLimit). Msg("Aborting reading packages because we hit the limit of packageNumberLimit") break } if packet == nil { log.Debug().Msg("Done reading packages. (nil returned)") break } if err := bar.Add(1); err != nil { log.Warn().Err(err).Msg("Error updating progressBar") } packetTimestamp := packet.Metadata().Timestamp realPacketNumber := timestampToIndexMap[packetTimestamp] description := fmt.Sprintf("No.[%d] timestamp: %v, %s", realPacketNumber, packetTimestamp, pcapFile) packetInformation := common.PacketInformation{ PacketNumber: realPacketNumber, PacketTimestamp: packetTimestamp, Description: description, } if networkLayer, ok := packet.NetworkLayer().(*layers.IPv4); ok { packetInformation.SrcIp = networkLayer.SrcIP packetInformation.DstIp = networkLayer.DstIP } var payload []byte applicationLayer := packet.ApplicationLayer() if applicationLayer == nil { log.Info().Stringer("packetInformation", packetInformation).Int("realPacketNumber", realPacketNumber).Msg("No.[realPacketNumber] No application layer") } else { payload = applicationLayer.Payload() } log.Debug().Hex("payload", payload).Msg("Got payload") if config.ExtractConfigInstance.Verbosity > 1 { printPayload(packetInformation, payload) } } _, _ = fmt.Fprintf(stdout, "\n") log.Info(). Uint("currentPackageNum", currentPackageNum). Int("numberOfPackage", numberOfPackage). Int("parseFails", parseFails). Int("serializeFails", serializeFails). Int("compareFails", compareFails). Msg("Done evaluating currentPackageNum of numberOfPackage packages (parseFails failed to parse, serializeFails failed to serialize and compareFails failed in byte comparison)") return nil }