├── Makefile ├── README.md └── gotcpspy.go /Makefile: -------------------------------------------------------------------------------- 1 | all: format run 2 | 3 | file = gotcpspy 4 | 5 | fmt_flags = -w=true -tabs=false -tabwidth=2 6 | 7 | format: 8 | gofmt $(fmt_flags) $(file).go 9 | 10 | run: 11 | go run $(file).go $(args) 12 | 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a TCP/IP debugger/proxy allowing to intercept the network traffic. 2 | It can serve multiple connections in parallel. 3 | 4 | Prerequisites 5 | ============= 6 | 7 | This program is written in Go and requires at least the release 1. 8 | 9 | Usage 10 | ===== 11 | 12 | For example: 13 | 14 | go run gotcpspy.go -host=ftp.idsoftware.com -port 21 -listen_port=8081 15 | 16 | It starts running with the message: 17 | 18 | Start listening on port 8081 and forwarding data to ftp.idsoftware.com:21 19 | 20 | In a separate console you can run: 21 | 22 | telnet localhost 8081 23 | 24 | and enter something, for example, `USER test` ``, 25 | `PASS test@test.org` `` and finally `QUIT` ``. 26 | 27 | It should produce three logs. 28 | 29 | Bidirectional dump 30 | ------------------ 31 | 32 | `log-2012.04.20-12.10.12-0001-10.44.2.21-26311-192.246.40.185-21.log` 33 | 34 | Connected to ftp.idsoftware.com:21 at 2012.04.20-12.10.12 35 | Received (#0, 00000000) 24 bytes from 10.44.2.21-26311 36 | 00000000 32 32 30 2d 66 74 70 2e 69 64 73 6f 66 74 77 61 |220-ftp.idsoftwa| 37 | 00000010 72 65 2e 63 6f 6d 0d 0a |re.com..| 38 | Sent (#0) to 127.0.0.1-8081 39 | Received (#1, 00000018) 353 bytes from 10.44.2.21-26311 40 | 00000000 32 32 30 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d |220-------------| 41 | 00000010 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d |----------------| 42 | 00000020 2d 0d 0a 32 32 30 2d 57 65 6c 63 6f 6d 65 20 74 |-..220-Welcome t| 43 | 00000030 6f 20 66 74 70 2e 69 64 73 6f 66 74 77 61 72 65 |o ftp.idsoftware| 44 | 00000040 2e 63 6f 6d 0d 0a 32 32 30 2d 2d 2d 2d 2d 2d 2d |.com..220-------| 45 | 00000050 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d |----------------| 46 | 00000060 2d 2d 2d 2d 2d 2d 2d 0d 0a 32 32 30 2d 0d 0a 32 |-------..220-..2| 47 | 00000070 32 30 2d 43 6f 6e 6e 65 63 74 69 6f 6e 20 66 72 |20-Connection fr| 48 | 00000080 6f 6d 20 38 30 2e 31 36 39 2e 33 34 2e 31 39 34 |om 80.169.34.194| 49 | 00000090 20 6c 6f 67 67 65 64 0d 0a 32 32 30 2d 59 6f 75 | logged..220-You| 50 | 000000a0 20 61 72 65 20 75 73 65 72 20 32 30 20 6f 66 20 | are user 20 of | 51 | 000000b0 31 35 30 20 61 76 61 69 6c 61 62 6c 65 20 63 6f |150 available co| 52 | 000000c0 6e 6e 65 63 74 69 6f 6e 73 2e 0d 0a 32 32 30 2d |nnections...220-| 53 | 000000d0 0d 0a 32 32 30 2d 41 76 65 72 61 67 65 20 74 68 |..220-Average th| 54 | 000000e0 72 6f 75 67 68 70 75 74 20 66 6f 72 20 74 68 69 |roughput for thi| 55 | 000000f0 73 20 73 65 72 76 65 72 20 69 73 20 32 34 38 2e |s server is 248.| 56 | 00000100 38 38 37 20 4b 42 70 73 2e 0d 0a 32 32 30 2d 35 |887 KBps...220-5| 57 | 00000110 35 37 34 20 70 65 6f 70 6c 65 20 68 61 76 65 20 |574 people have | 58 | 00000120 76 69 73 69 74 65 64 20 74 68 69 73 20 73 69 74 |visited this sit| 59 | 00000130 65 20 69 6e 20 74 68 65 20 6c 61 73 74 20 32 34 |e in the last 24| 60 | 00000140 20 68 6f 75 72 73 2e 0d 0a 32 32 30 2d 0d 0a 32 | hours...220-..2| 61 | 00000150 32 30 2d 0d 0a 32 32 30 2d 0d 0a 32 32 30 20 0d |20-..220-..220 .| 62 | 00000160 0a |.| 63 | Sent (#1) to 127.0.0.1-8081 64 | Received (#0, 00000000) 1 bytes from 127.0.0.1-8081 65 | 00000000 55 |U| 66 | Sent (#0) to 10.44.2.21-26311 67 | Received (#1, 00000001) 1 bytes from 127.0.0.1-8081 68 | 00000000 53 |S| 69 | Sent (#1) to 10.44.2.21-26311 70 | Received (#2, 00000002) 10 bytes from 127.0.0.1-8081 71 | 00000000 45 52 20 61 6e 6f 6e 79 6d 6f |ER anonymo| 72 | Sent (#2) to 10.44.2.21-26311 73 | Received (#3, 0000000C) 2 bytes from 127.0.0.1-8081 74 | 00000000 75 73 |us| 75 | Sent (#3) to 10.44.2.21-26311 76 | Received (#4, 0000000E) 2 bytes from 127.0.0.1-8081 77 | 00000000 0d 0a |..| 78 | Sent (#4) to 10.44.2.21-26311 79 | Received (#2, 00000179) 70 bytes from 10.44.2.21-26311 80 | 00000000 33 33 31 20 55 73 65 72 20 6e 61 6d 65 20 6f 6b |331 User name ok| 81 | 00000010 61 79 2c 20 70 6c 65 61 73 65 20 73 65 6e 64 20 |ay, please send | 82 | 00000020 63 6f 6d 70 6c 65 74 65 20 45 2d 6d 61 69 6c 20 |complete E-mail | 83 | 00000030 61 64 64 72 65 73 73 20 61 73 20 70 61 73 73 77 |address as passw| 84 | 00000040 6f 72 64 2e 0d 0a |ord...| 85 | Sent (#2) to 127.0.0.1-8081 86 | Received (#5, 00000010) 1 bytes from 127.0.0.1-8081 87 | 00000000 50 |P| 88 | Sent (#5) to 10.44.2.21-26311 89 | Received (#6, 00000011) 3 bytes from 127.0.0.1-8081 90 | 00000000 41 53 53 |ASS| 91 | Sent (#6) to 10.44.2.21-26311 92 | Received (#7, 00000014) 2 bytes from 127.0.0.1-8081 93 | 00000000 20 74 | t| 94 | Sent (#7) to 10.44.2.21-26311 95 | Received (#8, 00000016) 1 bytes from 127.0.0.1-8081 96 | 00000000 65 |e| 97 | Sent (#8) to 10.44.2.21-26311 98 | Received (#9, 00000017) 1 bytes from 127.0.0.1-8081 99 | 00000000 73 |s| 100 | Sent (#9) to 10.44.2.21-26311 101 | Received (#10, 00000018) 2 bytes from 127.0.0.1-8081 102 | 00000000 74 40 |t@| 103 | Sent (#10) to 10.44.2.21-26311 104 | Received (#11, 0000001A) 6 bytes from 127.0.0.1-8081 105 | 00000000 6e 61 6d 65 2e 6f |name.o| 106 | Sent (#11) to 10.44.2.21-26311 107 | Received (#12, 00000020) 2 bytes from 127.0.0.1-8081 108 | 00000000 72 67 |rg| 109 | Sent (#12) to 10.44.2.21-26311 110 | Received (#13, 00000022) 2 bytes from 127.0.0.1-8081 111 | 00000000 0d 0a |..| 112 | Sent (#13) to 10.44.2.21-26311 113 | Received (#3, 000001BF) 30 bytes from 10.44.2.21-26311 114 | 00000000 32 33 30 20 55 73 65 72 20 6c 6f 67 67 65 64 20 |230 User logged | 115 | 00000010 69 6e 2c 20 70 72 6f 63 65 65 64 2e 0d 0a |in, proceed...| 116 | Sent (#3) to 127.0.0.1-8081 117 | Received (#14, 00000024) 1 bytes from 127.0.0.1-8081 118 | 00000000 51 |Q| 119 | Sent (#14) to 10.44.2.21-26311 120 | Received (#15, 00000025) 3 bytes from 127.0.0.1-8081 121 | 00000000 55 49 54 |UIT| 122 | Sent (#15) to 10.44.2.21-26311 123 | Received (#16, 00000028) 2 bytes from 127.0.0.1-8081 124 | 00000000 0d 0a |..| 125 | Sent (#16) to 10.44.2.21-26311 126 | Received (#4, 000001DD) 14 bytes from 10.44.2.21-26311 127 | 00000000 32 32 31 20 47 6f 6f 64 62 79 65 21 0d 0a |221 Goodbye!..| 128 | Sent (#4) to 127.0.0.1-8081 129 | Disconnected from 10.44.2.21-26311 130 | Disconnected from 127.0.0.1-8081 131 | Finished at 2012.04.20-12.10.12, duration 16.5769481s 132 | 133 | Outgoing binary log 134 | ------------------- 135 | 136 | `log-binary-2012.04.20-12.10.12-0001-10.44.2.21-26311.log` 137 | 138 | USER anonymous 139 | PASS test@name.org 140 | QUIT 141 | 142 | Incoming binary log 143 | ------------------- 144 | 145 | `log-binary-2012.04.20-12.10.12-0001-192.246.40.185-21.log` 146 | 147 | 220-ftp.idsoftware.com 148 | 220------------------------------ 149 | 220-Welcome to ftp.idsoftware.com 150 | 220------------------------------ 151 | 220- 152 | 220-Connection from 80.169.34.194 logged 153 | 220-You are user 20 of 150 available connections. 154 | 220- 155 | 220-Average throughput for this server is 248.887 KBps. 156 | 220-5574 people have visited this site in the last 24 hours. 157 | 220- 158 | 220- 159 | 220- 160 | 220 161 | 331 User name okay, please send complete E-mail address as password. 162 | 230 User logged in, proceed. 163 | 221 Goodbye! 164 | -------------------------------------------------------------------------------- /gotcpspy.go: -------------------------------------------------------------------------------- 1 | // TCP/IP debugger/proxy 2 | // Copyright (C) 2012 by Alexander Demin 3 | 4 | package main 5 | 6 | import ( 7 | "encoding/hex" 8 | "flag" 9 | "fmt" 10 | "net" 11 | "os" 12 | "runtime" 13 | "strings" 14 | "time" 15 | ) 16 | 17 | var ( 18 | host *string = flag.String("host", "", "target host or address") 19 | port *string = flag.String("port", "0", "target port") 20 | listen_port *string = flag.String("listen_port", "0", "listen port") 21 | ) 22 | 23 | func die(format string, v ...interface{}) { 24 | os.Stderr.WriteString(fmt.Sprintf(format+"\n", v...)) 25 | os.Exit(1) 26 | } 27 | 28 | func connection_logger(data chan []byte, conn_n int, local_info, remote_info string) { 29 | log_name := fmt.Sprintf("log-%s-%04d-%s-%s.log", format_time(time.Now()), conn_n, local_info, remote_info) 30 | logger_loop(data, log_name) 31 | } 32 | 33 | func binary_logger(data chan []byte, conn_n int, peer string) { 34 | log_name := fmt.Sprintf("log-binary-%s-%04d-%s.log", format_time(time.Now()), conn_n, peer) 35 | logger_loop(data, log_name) 36 | } 37 | 38 | func logger_loop(data chan []byte, log_name string) { 39 | f, err := os.Create(log_name) 40 | if err != nil { 41 | die("Unable to create file %s, %v\n", log_name, err) 42 | } 43 | defer f.Close() 44 | for { 45 | b := <-data 46 | if len(b) == 0 { 47 | break 48 | } 49 | f.Write(b) 50 | f.Sync() 51 | } 52 | } 53 | 54 | func format_time(t time.Time) string { 55 | return t.Format("2006.01.02-15.04.05") 56 | } 57 | 58 | func printable_addr(a net.Addr) string { 59 | return strings.Replace(a.String(), ":", "-", -1) 60 | } 61 | 62 | type Channel struct { 63 | from, to net.Conn 64 | logger, binary_logger chan []byte 65 | ack chan bool 66 | } 67 | 68 | func pass_through(c *Channel) { 69 | from_peer := printable_addr(c.from.LocalAddr()) 70 | to_peer := printable_addr(c.to.LocalAddr()) 71 | 72 | b := make([]byte, 10240) 73 | offset := 0 74 | packet_n := 0 75 | for { 76 | n, err := c.from.Read(b) 77 | if err != nil { 78 | c.logger <- []byte(fmt.Sprintf("Disconnected from %s\n", from_peer)) 79 | break 80 | } 81 | if n > 0 { 82 | c.logger <- []byte(fmt.Sprintf("Received (#%d, %08X) %d bytes from %s\n", packet_n, offset, n, from_peer)) 83 | c.logger <- []byte(hex.Dump(b[:n])) 84 | c.binary_logger <- b[:n] 85 | c.to.Write(b[:n]) 86 | c.logger <- []byte(fmt.Sprintf("Sent (#%d) to %s\n", packet_n, to_peer)) 87 | offset += n 88 | packet_n += 1 89 | } 90 | } 91 | c.from.Close() 92 | c.to.Close() 93 | c.ack <- true 94 | } 95 | 96 | func process_connection(local net.Conn, conn_n int, target string) { 97 | remote, err := net.Dial("tcp", target) 98 | if err != nil { 99 | fmt.Printf("Unable to connect to %s, %v\n", target, err) 100 | } 101 | 102 | local_info := printable_addr(remote.LocalAddr()) 103 | remote_info := printable_addr(remote.RemoteAddr()) 104 | 105 | started := time.Now() 106 | 107 | logger := make(chan []byte) 108 | from_logger := make(chan []byte) 109 | to_logger := make(chan []byte) 110 | ack := make(chan bool) 111 | 112 | go connection_logger(logger, conn_n, local_info, remote_info) 113 | go binary_logger(from_logger, conn_n, local_info) 114 | go binary_logger(to_logger, conn_n, remote_info) 115 | 116 | logger <- []byte(fmt.Sprintf("Connected to %s at %s\n", target, format_time(started))) 117 | 118 | go pass_through(&Channel{remote, local, logger, to_logger, ack}) 119 | go pass_through(&Channel{local, remote, logger, from_logger, ack}) 120 | <-ack // Make sure that the both copiers gracefully finish. 121 | <-ack // 122 | 123 | finished := time.Now() 124 | duration := finished.Sub(started) 125 | logger <- []byte(fmt.Sprintf("Finished at %s, duration %s\n", format_time(started), duration.String())) 126 | 127 | logger <- []byte{} // Stop logger 128 | from_logger <- []byte{} // Stop "from" binary logger 129 | to_logger <- []byte{} // Stop "to" binary logger 130 | } 131 | 132 | func main() { 133 | runtime.GOMAXPROCS(runtime.NumCPU()) 134 | flag.Parse() 135 | if flag.NFlag() != 3 { 136 | fmt.Printf("usage: gotcpspy -host target_host -port target_port -listen_post=local_port\n") 137 | flag.PrintDefaults() 138 | os.Exit(1) 139 | } 140 | target := net.JoinHostPort(*host, *port) 141 | fmt.Printf("Start listening on port %s and forwarding data to %s\n", *listen_port, target) 142 | ln, err := net.Listen("tcp", ":"+*listen_port) 143 | if err != nil { 144 | fmt.Printf("Unable to start listener, %v\n", err) 145 | os.Exit(1) 146 | } 147 | conn_n := 1 148 | for { 149 | if conn, err := ln.Accept(); err == nil { 150 | go process_connection(conn, conn_n, target) 151 | conn_n += 1 152 | } else { 153 | fmt.Printf("Accept failed, %v\n", err) 154 | } 155 | } 156 | } 157 | --------------------------------------------------------------------------------