package main import ( "bufio" "context" "flag" "fmt" "net" "os" "strings" "time" "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/google/gopacket/pcapgo" "github.com/ossrs/go-oryx-lib/errors" "github.com/ossrs/go-oryx-lib/logger" ) func main() { ctx := logger.WithContext(context.Background()) if err := doMain(ctx); err != nil { panic(err) } } func trace(format string, args ...interface{}) { fmt.Println(fmt.Sprintf(format, args...)) } func doMain(ctx context.Context) error { var doRE, doTrace, help bool var pauseNumber, abortNumber uint64 var filename string var server string flag.BoolVar(&help, "h", false, "whether show this help") flag.BoolVar(&help, "help", false, "whether show this help") flag.BoolVar(&doRE, "re", true, "whether do real-time emulation") flag.BoolVar(&doTrace, "trace", true, "whether trace the packet") flag.Uint64Var(&pauseNumber, "pause", 0, "the packet number to pause") flag.Uint64Var(&abortNumber, "abort", 0, "the packet number to abort") flag.StringVar(&filename, "f", "", "the pcap filename, like ./t.pcapng") flag.StringVar(&server, "s", "", "the server address, like 127.0.0.1:1935") flag.Parse() if help { flag.Usage() os.Exit(0) } if filename == "" || server == "" { flag.Usage() os.Exit(1) } logger.Tf(ctx, "Forward pcap %v to %v, re=%v, trace=%v, pause=%v, abort=%v", filename, server, doRE, doTrace, pauseNumber, abortNumber) f, err := os.Open(filename) if err != nil { return errors.Wrapf(err, "open pcap %v", filename) } defer f.Close() var source *gopacket.PacketSource if strings.HasSuffix(filename, ".pcap") { r, err := pcapgo.NewReader(f) if err != nil { return errors.Wrapf(err, "new reader") } source = gopacket.NewPacketSource(r, r.LinkType()) } else { r, err := pcapgo.NewNgReader(f, pcapgo.DefaultNgReaderOptions) if err != nil { return errors.Wrapf(err, "new reader") } source = gopacket.NewPacketSource(r, r.LinkType()) } // TODO: FIXME: Should start a goroutine to consume bytes from conn. conn, err := net.Dial("tcp", server) if err != nil { return errors.Wrapf(err, "dial %v", server) } defer conn.Close() var packetNumber uint64 var previousTime *time.Time for packet := range source.Packets() { packetNumber++ if packet.Layer(layers.LayerTypeTCP) == nil { continue } ci := packet.Metadata().CaptureInfo tcp, _ := packet.Layer(layers.LayerTypeTCP).(*layers.TCP) payload := tcp.Payload if len(payload) == 0 { continue } if tcp.DstPort != 1935 && tcp.DstPort != 19350 { continue } if pauseNumber > 0 && packetNumber == pauseNumber { reader := bufio.NewReader(os.Stdin) trace("#%v Press Enter to continue...", packetNumber) _, _ = reader.ReadString('\n') } if abortNumber > 0 && packetNumber > abortNumber { break } if _, err := conn.Write(payload); err != nil { return errors.Wrapf(err, "write to %v", server) } if doRE { if previousTime == nil { previousTime = &ci.Timestamp } else { if diff := ci.Timestamp.Sub(*previousTime); diff > 100*time.Millisecond { time.Sleep(diff) previousTime = &ci.Timestamp } } } if doTrace { trace("#%v TCP %v=>%v %v Len:%v", packetNumber, uint16(tcp.SrcPort), uint16(tcp.DstPort), ci.Timestamp.Format("15:04:05.000"), len(payload)) } } return nil }