Initial commit

This commit is contained in:
Yun Zheng Hu
2023-07-11 16:54:09 +02:00
commit 530840a66c
7 changed files with 534 additions and 0 deletions

174
cmd/pcap-broker/main.go Normal file
View File

@@ -0,0 +1,174 @@
package main
import (
"context"
"flag"
"fmt"
"log"
"net"
"os"
"os/exec"
"time"
"github.com/google/shlex"
"github.com/google/gopacket"
"github.com/google/gopacket/pcap"
"github.com/google/gopacket/pcapgo"
)
type PcapClient struct {
writer *pcapgo.Writer
totalPackets uint64
totalBytes uint64
}
func lookupHostnameWithTimeout(addr net.Addr, timeout time.Duration) (string, string, error) {
// Extract the IP address and port from the Addr object
tcpAddr, ok := addr.(*net.TCPAddr)
if !ok {
return "", "", fmt.Errorf("unsupported address type: %T", addr)
}
ip := tcpAddr.IP.String()
port := fmt.Sprintf("%d", tcpAddr.Port)
// Create a new context with the given timeout
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
// Create a new Resolver and perform the IP lookup with the given context
resolver := net.Resolver{}
names, err := resolver.LookupAddr(ctx, ip)
if err != nil {
return "", "", err
}
if len(names) == 0 {
return "", "", fmt.Errorf("no hostnames found for %s", ip)
}
// Return the first IP address found and the original port
return names[0], port, nil
}
func main() {
pcapCommand := flag.String("cmd", "", "command to execute for pcap data (eg: tcpdump -i eth0 -n --immediate-mode -s 65535 -U -w -)")
listenAddress := flag.String("listen", "", "listen address for pcap-over-ip (eg: localhost:4242)")
noReverseLookup := flag.Bool("n", false, "disable reverse lookup of connecting PCAP-over-IP client IP address")
flag.Parse()
if *pcapCommand == "" {
*pcapCommand = os.Getenv("PCAP_COMMAND")
if *pcapCommand == "" {
log.Fatalf("Error: PCAP_COMMAND or -cmd not set, see --help for usage")
}
}
if *listenAddress == "" {
*listenAddress = os.Getenv("LISTEN_ADDRESS")
if *listenAddress == "" {
*listenAddress = "localhost:4242"
}
}
log.Printf("config PCAP_COMMAND = %q", *pcapCommand)
log.Printf("config LISTEN_ADDRESS = %q", *listenAddress)
// Create connections to PcapClient map
var connMap = map[net.Conn]PcapClient{}
// Create a pipe for the command to write to, will be read by pcap.OpenOfflineFile
rStdout, wStdout, err := os.Pipe()
if err != nil {
log.Fatal(err)
}
// Important or these will eventually be garbage collected and the pipe will close
defer rStdout.Close()
defer wStdout.Close()
// Acquire pcap data
args, err := shlex.Split(*pcapCommand)
if err != nil {
log.Fatal(err)
}
cmd := exec.Command(args[0], args[1:]...)
log.Printf("cmd = %v", cmd.Args)
cmd.Stdout = wStdout
cmd.Stderr = os.Stderr
err = cmd.Start()
if err != nil {
log.Fatal(err)
}
log.Printf("PID %v", cmd.Process.Pid)
go func() {
err := cmd.Wait()
if err != nil {
log.Fatal("Process exited with error: ", err)
}
log.Printf("process exited")
os.Exit(0)
}()
// Read from process stdout pipe
handle, err := pcap.OpenOfflineFile(rStdout)
if err != nil {
log.Fatal(err)
}
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
packetSource.Lazy = true
packetSource.NoCopy = true
go processPackets(packetSource, connMap)
log.Printf("PCAP-over-IP server listening on %v", *listenAddress)
l, err := net.Listen("tcp", *listenAddress)
if err != nil {
log.Fatal(err)
}
for {
conn, err := l.Accept()
if err != nil {
log.Fatal(err)
}
if *noReverseLookup {
log.Printf("PCAP-over-IP connection from %v", conn.RemoteAddr())
} else {
ip, port, err := lookupHostnameWithTimeout(conn.RemoteAddr(), 100*time.Millisecond)
if err != nil {
log.Printf("PCAP-over-IP connection from %v", conn.RemoteAddr())
} else {
log.Printf("PCAP-over-IP connection from %s:%s", ip, port)
}
}
writer := pcapgo.NewWriter(conn)
// Write pcap header
writer.WriteFileHeader(65535, handle.LinkType())
// add connection to map
connMap[conn] = PcapClient{writer: writer}
}
}
func processPackets(packetSource *gopacket.PacketSource, connMap map[net.Conn]PcapClient) {
for packet := range packetSource.Packets() {
for conn, stats := range connMap {
ci := packet.Metadata().CaptureInfo
err := stats.writer.WritePacket(ci, packet.Data())
if err != nil {
log.Println(err)
delete(connMap, conn)
conn.Close()
continue
}
stats.totalPackets += 1
stats.totalBytes += uint64(ci.CaptureLength)
}
}
}