/* * Trap network connections by reading them a file indefinitely * * by wgs */ package main import ( "fmt" "flag" "io" "log" "net" "os" "bytes" "time" ) // Default to HTTP response (works with ssh too!) const BANNER = "HTTP/1.1 200 OK\n" const FORMAT = "X: %s" var count int var banner string var format string var delay int var msg []byte func usage() { fmt.Fprint(os.Stderr, "usage: tarpit [-l listen] [-d delay] [-f FORMAT] [-b BANNER] file") os.Exit(2) } // Write a formatted string, one byte at a time func delayPrint(c net.Conn, d time.Duration, p string, l string) error { b := fmt.Sprintf(p, l) for i := 0; i < len(b); i++ { time.Sleep(d) _, err := c.Write([]byte(b[i:i+1])) if err != nil { return err } } return nil } func tarpit(c net.Conn, delay int) { start := time.Now() addr := c.RemoteAddr().String() count++ log.Printf("%s connected (%d total)\n", addr, count); b := bytes.NewBuffer(msg) d := time.Duration(delay) * time.Millisecond fmt := format delayPrint(c, d, "%s", banner) for { l, err := b.ReadBytes('\n') if err == io.EOF { b = bytes.NewBuffer(msg) } else if err != nil { delayPrint(c, d, fmt, "It's your lucky day…") break } err = delayPrint(c, d, fmt, string(l)) if err != nil { break } } count-- log.Printf("%s disconnected (trapped %s)", addr, time.Since(start)); } func main() { var err error var laddr string flag.IntVar(&delay, "d", 1000, "Delay between bytes (ms)") flag.StringVar(&laddr, "l", ":1337", "Address to listen on") flag.StringVar(&banner, "b", BANNER, "Banner to send ahead of message") flag.StringVar(&format, "f", FORMAT, "Line format (%s is a file line)") flag.Parse() args := flag.Args() if len(args) != 1 { usage() } file := args[0] msg, err = os.ReadFile(file) if err != nil { log.Fatal(file, err) } l, err := net.Listen("tcp", laddr) if err != nil { log.Fatal(err) } // wait for connections for { c, err := l.Accept() if err != nil { log.Println(err) continue } // handle connections concurently go tarpit(c, delay) } }