diff --git a/common/common.go b/common/common.go index 1ec31d9..abe2e51 100644 --- a/common/common.go +++ b/common/common.go @@ -16,15 +16,20 @@ func AppendIfUnique(appendee string, array []string) []string { return append(array, appendee) } -// Prints each element, along with a small ASCII tree -func PrintTree(strarr []string) { +// Generates a small ASCII tree for the given string array +func GenerateTree(strarr []string) string { + tmpstr := "" + // iterate over each element for iter, elem := range strarr { // check if we got the last element if iter < len(strarr) - 1 { - fmt.Printf("|- %s\n", elem) + tmpstr = fmt.Sprintf("%s|- %s\n", tmpstr, elem) } else { - fmt.Printf("'- %s\n\n", elem) + tmpstr = fmt.Sprintf( "%s'- %s\n", tmpstr, elem) } } + + // Return constructed (grown?) tree + return tmpstr } diff --git a/ethernet/arp/arp.go b/ethernet/arp/arp.go index 09630a0..9054aff 100644 --- a/ethernet/arp/arp.go +++ b/ethernet/arp/arp.go @@ -3,7 +3,7 @@ package arp import ( "fmt" "git.darknebu.la/maride/pancap/common" - "github.com/fatih/color" + "git.darknebu.la/maride/pancap/output" "github.com/google/gopacket" "github.com/google/gopacket/layers" "log" @@ -52,15 +52,12 @@ func ProcessARPPacket(packet gopacket.Packet) error { // Print a summary after all packets are processed func PrintARPSummary() { - headline := color.New(color.FgRed, color.Bold) - headline.Println("ARP traffic summary") - printTrafficStats() - headline.Println("ARP LAN overview") - printLANOverview() + output.PrintBlock("ARP traffic summary", generateTrafficStats()) + output.PrintBlock("ARP LAN overview", generateLANOverview()) } -// Constructs an answer regarding the ARP traffic -func printTrafficStats() { +// Generates an answer regarding the ARP traffic +func generateTrafficStats() string { var tmparr []string // Iterate over all participants @@ -82,11 +79,11 @@ func printTrafficStats() { } // And print it as a tree - common.PrintTree(tmparr) + return common.GenerateTree(tmparr) } -// Prints an overview over all connected devices in the LAN -func printLANOverview() { +// Generates an overview over all connected devices in the LAN +func generateLANOverview() string { var tmparr []string // iterate over all devices @@ -95,7 +92,7 @@ func printLANOverview() { } // And print it as a tree - common.PrintTree(tmparr) + return common.GenerateTree(tmparr) } // Returns the arpStats object for the given MAC address, or creates a new one diff --git a/ethernet/dhcpv4/dhcp.go b/ethernet/dhcpv4/dhcp.go index 1b26f69..dc25349 100644 --- a/ethernet/dhcpv4/dhcp.go +++ b/ethernet/dhcpv4/dhcp.go @@ -1,7 +1,7 @@ package dhcpv4 import ( - "github.com/fatih/color" + "git.darknebu.la/maride/pancap/output" "github.com/google/gopacket" "github.com/google/gopacket/layers" ) @@ -50,13 +50,8 @@ func HandleDHCPv4Packet(packet gopacket.Packet) error { // Print summary after all packets are processed func PrintDHCPv4Summary() { - headline := color.New(color.FgRed, color.Bold) - headline.Println("DHCP Network Overview") - printNetworkSummary() - headline.Println("DHCP Requests") - printRequestSummary() - headline.Println("DHCP Responses/Offers") - printResponseSummary() - headline.Println("DHCP Hostnames") - printHostnames() + output.PrintBlock("DHCP Network Overview", generateNetworkSummary()) + output.PrintBlock("DHCP Requests", generateRequestSummary()) + output.PrintBlock("DHCP Responses/Offers", generateResponseSummary()) + output.PrintBlock("DHCP Hostnames", generateHostnamesSummary()) } diff --git a/ethernet/dhcpv4/hostnames.go b/ethernet/dhcpv4/hostnames.go index 89b9d98..76793ea 100644 --- a/ethernet/dhcpv4/hostnames.go +++ b/ethernet/dhcpv4/hostnames.go @@ -39,8 +39,8 @@ func checkForHostname(dhcppacket layers.DHCPv4) { // None found, means client or server doesn't support Hostname option field. Ignore. } -// Prints the list of all hostnames encountered. -func printHostnames() { +// Generates the list of all hostnames encountered. +func generateHostnamesSummary() string { var tmparr []string // Construct meaningful text @@ -72,7 +72,7 @@ func printHostnames() { } // and print it as a tree. - common.PrintTree(tmparr) + return common.GenerateTree(tmparr) } // Adds the given hostname to the hostname array, or patches an existing entry if found diff --git a/ethernet/dhcpv4/network.go b/ethernet/dhcpv4/network.go index aaf4d52..907c597 100644 --- a/ethernet/dhcpv4/network.go +++ b/ethernet/dhcpv4/network.go @@ -140,14 +140,16 @@ func formatDate(rawDate []byte) string { return formattedDate } -// Prints the summary of relevant DHCP options -func printNetworkSummary() { - fmt.Printf("Subnet Mask: %s\n", formatIP(networkSetup[layers.DHCPOptSubnetMask])) - fmt.Printf("Broadcast: %s\n", formatIP(networkSetup[layers.DHCPOptBroadcastAddr])) - fmt.Printf("Router: %s\n", formatIP(networkSetup[layers.DHCPOptRouter])) - fmt.Printf("DNS Server: %s\n", formatIP(networkSetup[layers.DHCPOptDNS])) - fmt.Printf("NTP Server: %s\n", formatIP(networkSetup[layers.DHCPOptNTPServers])) - fmt.Printf("Lease Time: %s\n", formatDate(networkSetup[layers.DHCPOptLeaseTime])) - fmt.Printf("Renewal Time: %s\n", formatDate(networkSetup[layers.DHCPOptT1])) +// Generates the summary of relevant DHCP options +func generateNetworkSummary() string { + // It's also possible to use strings.Builder here, but it produces code which is longer than this solution :shrug: + summary := fmt.Sprintf("Subnet Mask: %s\n", formatIP(networkSetup[layers.DHCPOptSubnetMask])) + summary = fmt.Sprintf("%sBroadcast: %s\n", summary, formatIP(networkSetup[layers.DHCPOptBroadcastAddr])) + summary = fmt.Sprintf("%sRouter: %s\n", summary, formatIP(networkSetup[layers.DHCPOptRouter])) + summary = fmt.Sprintf("%sDNS Server: %s\n", summary, formatIP(networkSetup[layers.DHCPOptDNS])) + summary = fmt.Sprintf("%sNTP Server: %s\n", summary, formatIP(networkSetup[layers.DHCPOptNTPServers])) + summary = fmt.Sprintf("%sLease Time: %s\n", summary, formatDate(networkSetup[layers.DHCPOptLeaseTime])) + summary = fmt.Sprintf("%sRenewal Time: %s\n", summary, formatDate(networkSetup[layers.DHCPOptT1])) + return summary } diff --git a/ethernet/dhcpv4/request.go b/ethernet/dhcpv4/request.go index ba6a787..85b5fb1 100644 --- a/ethernet/dhcpv4/request.go +++ b/ethernet/dhcpv4/request.go @@ -15,8 +15,7 @@ func processRequestPacket(dhcppacket layers.DHCPv4) { requestMAC = common.AppendIfUnique(dhcppacket.ClientHWAddr.String(), requestMAC) } -// Prints the summary of all DHCP request packets -func printRequestSummary() { - fmt.Printf("%d unique DHCP requests\n", len(requestMAC)) - common.PrintTree(requestMAC) +// Generates the summary of all DHCP request packets +func generateRequestSummary() string { + return fmt.Sprintf("%d unique DHCP requests\n%s", len(requestMAC), common.GenerateTree(requestMAC)) } diff --git a/ethernet/dhcpv4/response.go b/ethernet/dhcpv4/response.go index 74e65bb..fa80637 100644 --- a/ethernet/dhcpv4/response.go +++ b/ethernet/dhcpv4/response.go @@ -15,8 +15,8 @@ func processResponsePacket(dhcppacket layers.DHCPv4, ethernetpacket layers.Ether addResponseEntry(dhcppacket.ClientIP.String(), dhcppacket.YourClientIP.String(), dhcppacket.ClientHWAddr.String(), ethernetpacket.SrcMAC.String()) } -// Prints the summary of all DHCP offer packets -func printResponseSummary() { +// Generates the summary of all DHCP offer packets +func generateResponseSummary() string { var tmpaddr []string // Iterate over all responses @@ -31,7 +31,7 @@ func printResponseSummary() { } // Draw as tree - common.PrintTree(tmpaddr) + return common.GenerateTree(tmpaddr) } // Adds a new response entry. If an IP address was already issued or a MAC asks multiple times for DNS, the case is examined further diff --git a/ethernet/dns/answer.go b/ethernet/dns/answer.go index 9fcfb87..ab99d51 100644 --- a/ethernet/dns/answer.go +++ b/ethernet/dns/answer.go @@ -62,29 +62,31 @@ func processDNSAnswer(answers []layers.DNSResourceRecord) { } } -// Prints a summary of all DNS answers -func printDNSAnswerSummary() { +// Generates a summary of all DNS answers +func generateDNSAnswerSummary() string { + summary := "" + // Overall question stats - fmt.Printf("%d DNS answers in total\n", numAnswers) - fmt.Printf("%s records\n", generateDNSTypeSummary(answerType)) - fmt.Printf("%d unique domains of %d base domains, of which are %d private (non-ICANN) TLDs.\n", len(answerDomains), len(answerBaseDomains), len(answerPrivateDomains)) + summary = fmt.Sprintf("%s%d DNS answers in total\n", summary, numAnswers) + summary = fmt.Sprintf("%s%s records\n", summary, generateDNSTypeSummary(answerType)) + summary = fmt.Sprintf("%s%d unique domains of %d base domains, of which are %d private (non-ICANN) TLDs.\n", summary, len(answerDomains), len(answerBaseDomains), len(answerPrivateDomains)) // Output base domains answered with if len(answerBaseDomains) > 0 { - fmt.Println("Answered with these base domains:") - common.PrintTree(answerBaseDomains) + summary = fmt.Sprintf("Answered with these base domains:\n%s", common.GenerateTree(answerBaseDomains)) } // Output private domains if len(answerPrivateDomains) > 0 { - fmt.Println("Answered with these private (non-ICANN managed) domains:") - common.PrintTree(answerPrivateDomains) + summary = fmt.Sprintf("%sAnswered with these private (non-ICANN managed) domains:\n%s", summary, common.GenerateTree(answerPrivateDomains)) } // Check for public and private IPs - fmt.Printf("Answered with %d public IP addresses and %d private IP addresses\n", len(answerPublicIPv4), len(answerPrivateIPv4)) + summary = fmt.Sprintf("%sAnswered with %d public IP addresses and %d private IP addresses\n", summary, len(answerPublicIPv4), len(answerPrivateIPv4)) if len(answerPrivateIPv4) > 0 { - fmt.Println("Private IP addresses in answer:") - common.PrintTree(answerPrivateIPv4) + summary = fmt.Sprintf("%sPrivate IP addresses in answer:\n%s", summary, common.GenerateTree(answerPrivateIPv4)) } + + // Return summary + return summary } diff --git a/ethernet/dns/dns.go b/ethernet/dns/dns.go index 73a1148..f4ba6af 100644 --- a/ethernet/dns/dns.go +++ b/ethernet/dns/dns.go @@ -1,7 +1,7 @@ package dns import ( - "github.com/fatih/color" + "git.darknebu.la/maride/pancap/output" "github.com/google/gopacket" "github.com/google/gopacket/layers" ) @@ -27,9 +27,6 @@ func ProcessDNSPacket(packet gopacket.Packet) error { // Print a summary after all DNS packets were processed func PrintDNSSummary() { - headline := color.New(color.FgRed, color.Bold) - headline.Println("DNS Request Summary") - printDNSQuestionSummary() - headline.Println("DNS Response Summary") - printDNSAnswerSummary() + output.PrintBlock("DNS Request Summary", generateDNSQuestionSummary()) + output.PrintBlock("DNS Response Summary", generateDNSAnswerSummary()) } diff --git a/ethernet/dns/question.go b/ethernet/dns/question.go index c34ca59..04d5f70 100644 --- a/ethernet/dns/question.go +++ b/ethernet/dns/question.go @@ -51,22 +51,25 @@ func processDNSQuestion(questions []layers.DNSQuestion) { } } -// Prints a summary of all DNS questions -func printDNSQuestionSummary() { +// Generates a summary of all DNS questions +func generateDNSQuestionSummary() string { + summary := "" + // Overall question stats - fmt.Printf("%d DNS questions in total\n", numQuestions) - fmt.Printf("%s records\n", generateDNSTypeSummary(questionType)) - fmt.Printf("%d unique domains of %d base domains, of which are %d private (non-ICANN) TLDs.\n", len(questionDomains), len(questionBaseDomains), len(questionPrivateDomains)) + summary = fmt.Sprintf("%s%d DNS questions in total\n", summary, numQuestions) + summary = fmt.Sprintf("%s%s records\n", summary, generateDNSTypeSummary(questionType)) + summary = fmt.Sprintf("%s%d unique domains of %d base domains, of which are %d private (non-ICANN) TLDs.\n", summary, len(questionDomains), len(questionBaseDomains), len(questionPrivateDomains)) // Output base domains asked for if len(questionBaseDomains) > 0 { - fmt.Println("Asked for these base domains:") - common.PrintTree(questionBaseDomains) + summary = fmt.Sprintf("%sAsked for these base domains:\n%s", summary, common.GenerateTree(questionBaseDomains)) } // Output private domains if len(questionPrivateDomains) > 0 { - fmt.Println("Asked for these private (non-ICANN managed) domains:") - common.PrintTree(questionPrivateDomains) + summary = fmt.Sprintf("%sAsked for these private (non-ICANN managed) domains:\n%s", summary, common.GenerateTree(questionPrivateDomains)) } + + // And return summary + return summary } \ No newline at end of file diff --git a/output/output.go b/output/output.go new file mode 100644 index 0000000..2f6757d --- /dev/null +++ b/output/output.go @@ -0,0 +1,52 @@ +package output + +import ( + "fmt" + "github.com/fatih/color" +) + +const ( + MaxContentLines = 50 +) + +// Prints a block of information with the given headline +// If content is empty, printing the headline is omitted. +// If the content is longer than MaxContentLines, content is cut. +func PrintBlock(headline string, content string) { + // Print a newline to add some space between blocks + fmt.Println("") + + // Check if we need to print a headline + if len(content) > 0 { + // We have content, we can print the headline + headlineColor := color.New(color.FgRed, color.Bold) + headlineColor.Println(headline) + } + + // Cut to MaxContentLines if required + cutCont := cutContent(content) + fmt.Print(cutCont) +} + +// Cut content after MaxContentLines lines +func cutContent(content string) string { + numNewlines := 0 + + // iterate over every character + for i, c := range content { + // check if character is newline + if c == '\n' { + // it is, count occurrence + numNewlines++ + + // Check if we already hit our limit yet + if numNewlines == MaxContentLines { + // Found nth newline, return content up to this position. + return content[:i+1] // +1 to include the last newline as well + } + } + } + + // We are done before reaching the cut limit; return the whole content + return content +} \ No newline at end of file