├── .gitignore ├── Dockerfile ├── Makefile ├── README.md ├── config.json ├── docker-compose.yml ├── go.mod └── tcpprox.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | tcpprox 3 | server.* 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.7.3 as builder 2 | 3 | COPY . $GOPATH/src/github.com/staaldraad/tcpprox/ 4 | WORKDIR $GOPATH/src/github.com/staaldraad/tcpprox/ 5 | 6 | RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -ldflags="-w -s" -o /go/bin/tcpprox 7 | 8 | FROM scratch 9 | COPY --from=builder /go/bin/tcpprox /go/bin/tcpprox 10 | ENTRYPOINT [ "/go/bin/tcpprox" ] 11 | 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | go build -o tcpprox tcpprox.go 3 | 4 | run: 5 | go run tcpprox.go 6 | 7 | compile: 8 | # 32-bit 9 | # Linux 10 | GOOS=linux GOARCH=386 go build -o tcpprox-linux86 11 | sha256sum tcpprox-linux86 > tcpprox-linux86.sha256sum 12 | # Windows 13 | GOOS=windows GOARCH=386 go build -o tcpprox-win86.exe 14 | sha256sum tcpprox-win86.exe > tcpprox-win86.sha256sum 15 | 16 | # 64-bit 17 | # Linux 18 | GOOS=linux GOARCH=amd64 go build -o tcpprox-linux64 19 | sha256sum tcpprox-linux64 > tcpprox-linux64.sha256sum 20 | # Windows 21 | GOOS=windows GOARCH=amd64 go build -o tcpprox-win64.exe 22 | sha256sum tcpprox-win64.exe > tcpprox-win64.sha256sum 23 | # OSX 24 | GOOS=darwin GOARCH=arm64 go build -o tcpprox-osxarm64 25 | sha256sum tcpprox-osxarm64 > tcpprox-osxarm64.sha256sum 26 | GOOS=darwin GOARCH=amd64 go build -o tcpprox-osx64 27 | sha256sum tcpprox-osx64 > tcpprox-osx64.sha256sum 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tcpprox 2 | 3 | 4 | A simple TCP proxy written in GO. Allows for proxy-ing TCP connections as well as TLS wrapped TCP connections. 5 | 6 | Can be run simply from the command-line using arguments. Or supplying a config file. Or both. 7 | _The command line arguments have precidence and override the config file_ 8 | 9 | # Usage 10 | 11 | To create a TLS proxy using the supplied config file: 12 | 13 | ``` 14 | tcpprox -s -c config.json -r 172.16.0.12:4550 15 | ``` 16 | 17 | To create a normal TCP proxy, no config file: 18 | 19 | ``` 20 | tcpprox -l 0.0.0.0 -p 8081 -r 172.16.0.12:8081 21 | ``` 22 | 23 | To specify a custom certificate to use (PEM format) you can use the -cert and -key options (must be used together): 24 | 25 | ___Note (breaking change)__ for previous versions of tcpprox, the `-cert` and `-key` arguments were combined into one argument `-cert`. This previous arg would take the supplied value and automatically append **.pem** and **.key**. This is no longer the case and the supplied filepaths for `-cert` and `-key` must be complete and for valid, matching files._ 26 | 27 | ``` 28 | tcpprox -s -c config.json -cert server.pem -key server.key 29 | ``` 30 | 31 | To generate valid certificate and key: 32 | 33 | ``` 34 | openssl genrsa -out server.key 2048 35 | openssl req -new -x509 -key server.key -out server.pem -days 3650 36 | ``` 37 | 38 | To convert the certificate to DER format: 39 | 40 | ``` 41 | openssl x509 -in server.pem -out server.crt -outform der 42 | ``` 43 | 44 | 45 | ## mTLS 46 | 47 | If proxying a connection where the upstream server uses mTLS, tcpprox can be configured to use mTLS to authenticate. 48 | 49 | ``` 50 | tcpprox -s -c config.json -cert server.pem -key server.key -clientCert client.pem -clientKey client.key 51 | ``` 52 | 53 | The application that is being proxied through tcpprox does not have to supply a client certificate (although it can). 54 | 55 | This allows for either: 56 | 57 | ``` 58 | client <---TLS---> tcpprox <---mTLS---> server 59 | ``` 60 | 61 | or 62 | 63 | ``` 64 | client <---mTLS---> tcpprox <---mTLS---> server 65 | ``` 66 | 67 | Tcpprox will allow both types of connections through, as long as tcpprox is able to use mTLS to connect to the server, the client is oblivious of what is happening upstream. 68 | 69 | ## Config File 70 | 71 | The config file can be used instead of supplying all information on the command line. The options specified in the file will be overwritten by any matching command line arguments. This allows for using a config file and overriding one or more options for testing / variation between hosts. 72 | 73 | # Using Docker 74 | 75 | _note_: ensure you are running 'real' Docker [docker-ce](https://docs.docker.com/install/#supported-platforms) 76 | 77 | **To build locally with Docker:** 78 | 79 | ``` 80 | docker build . -t staaldraad/tcpprox:latest 81 | ``` 82 | 83 | Or, even better, just get it directly off of Docker Hub 84 | 85 | **Get from Docker Hub** 86 | 87 | ``` 88 | docker pull staaldraad/tcpprox:latest 89 | ``` 90 | 91 | **Run the container:** 92 | 93 | ``` 94 | docker run -it --rm -p 8000:8000 staaldraad/tcpprox:latest -p 8000 -s -r google.com:443 95 | ``` 96 | 97 | This will create a TLS enabled listener on port 8000 and proxy traffic to google.com:443 98 | 99 | 100 | **Something about performance** 101 | 102 | Method of outputting (displaying) the data being proxied determines how close to "non-proxied" performance you get. 103 | 104 | * Running with `-raw -o /path/to/file` gives near native speed. AKA, it is as if the proxy isn't there. 105 | * Running with `-o /path/to/file` is almost as fast 106 | * Outputting the hex.dump directly to screen is the slowest option. To get better speed, output hex to a file and use `tail -f /path/to/file`. It is still a little slower (depending on hardware) but better than direct to screen. 107 | 108 | Direct to screen with hex dump formatting (default): `./tcproxy -p 8000 user@remote` 109 | ``` 110 | $ scp -P 8000 root@localhost:file.txt /tmp/p 111 | file.txt 100% 10MB 2.1MB/s 00:04 112 | ``` 113 | 114 | Write to file with hex dump formatting: `./tcproxy -p 8000 user@remote -o /tmp/session` 115 | ``` 116 | $ scp -P 8000 root@localhost:file.txt /tmp/p 117 | file.txt 100% 10MB 10.2MB/s 00:00 118 | ``` 119 | 120 | Write raw stream to file: `./tcproxy -p 8000 user@remote -o /tmp/session -raw` 121 | ``` 122 | $ scp -P 8000 root@localhost:file.txt /tmp/p 123 | file.txt 100% 10MB 10.7MB/s 00:00 124 | 125 | ``` 126 | 127 | Write to file with hex dump formatting: `./tcproxy -p 8000 user@remote -o /tmp/session ` and use tail to view live stream: `tail -f /tmp/session` 128 | ``` 129 | $ scp -P 8000 root@localhost:file.txt /tmp/p 130 | file.txt 100% 10MB 5.9MB/s 00:01 131 | 132 | ``` 133 | 134 | And raw (no output at all): `./tcproxy -p 8000 user@remote -raw` 135 | 136 | ``` 137 | $ scp -P 8000 root@localhost:file.txt /tmp/p 138 | file.txt 100% 10MB 20.2MB/s 00:00 139 | ``` -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "remotehost":"", 3 | "localhost":"0.0.0.0", 4 | "localport":1990, 5 | "TLS":{ 6 | "Country":["GB"], 7 | "Org":["Staaldraad"], 8 | "CommonName":"*.domain.com" 9 | }, 10 | "CACertFile":"", 11 | "CAKeyFile":"", 12 | "ClientCertFile":"", 13 | "ClientKeyFile":"", 14 | "IPS" : [], 15 | "Names" : [] 16 | } 17 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | # Example testing Docker Compose manifest (same as readme). Usage: 2 | # $ curl --insecure --header https://localhost:8000 3 | tcpprox: 4 | container_name: tcpprox 5 | build: . 6 | # image: staaldraad/tcpprox 7 | command: -p 8000 -s -r google.com:443 8 | ports: 9 | - "8000:8000" 10 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/staaldraad/tcpprox 2 | 3 | go 1.21.4 4 | -------------------------------------------------------------------------------- /tcpprox.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "crypto/rand" 6 | "crypto/rsa" 7 | "crypto/tls" 8 | "crypto/x509" 9 | "crypto/x509/pkix" 10 | "encoding/hex" 11 | "encoding/json" 12 | "flag" 13 | "fmt" 14 | "io" 15 | "net" 16 | "os" 17 | "sync" 18 | "time" 19 | ) 20 | 21 | type TLS struct { 22 | Country []string 23 | Org []string 24 | CommonName string 25 | } 26 | 27 | type Config struct { 28 | Remotehost string `json:"remotehost"` 29 | Localhost string `json:"localhost"` 30 | Localport int `json:"localport"` 31 | TLS *TLS `json:"TLS"` 32 | CACertFile string `json:"CACertFile"` 33 | CAKeyFile string `json:"CAKeyFile"` 34 | ClientCertFile string `json:"ClientCertFile"` // client cert for mTLS 35 | ClientKeyFile string `json:"ClientKeyFile"` // client priv key for mTLS 36 | ListenerMTLS bool `json:"ListenerMTLS"` // use the ClientKeyFile to set mTLS on the listener 37 | RichRaw bool `json:"RichRaw"` 38 | IPS []string // IPAddress for the child cert 39 | Names []string // DNSNames for the child cert 40 | Raw bool `json:"Raw"` 41 | ToFile string `json:"ToFile"` 42 | Quiet bool `json:"Quiet"` 43 | } 44 | 45 | var config Config 46 | var ids = 0 47 | var sessionFile *os.File 48 | 49 | func genCert() ([]byte, *rsa.PrivateKey) { 50 | s, _ := rand.Prime(rand.Reader, 128) 51 | ca := &x509.Certificate{ 52 | SerialNumber: s, 53 | Subject: pkix.Name{ 54 | Country: config.TLS.Country, 55 | Organization: config.TLS.Org, 56 | CommonName: config.TLS.CommonName, 57 | }, 58 | NotBefore: time.Now(), 59 | NotAfter: time.Now().AddDate(10, 0, 0), 60 | SubjectKeyId: []byte{1, 2, 3, 4, 5}, 61 | BasicConstraintsValid: true, 62 | IsCA: true, 63 | ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, 64 | KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, 65 | } 66 | 67 | priv, _ := rsa.GenerateKey(rand.Reader, 2048) 68 | pub := &priv.PublicKey 69 | ca_b, err := x509.CreateCertificate(rand.Reader, ca, ca, pub, priv) 70 | if err != nil { 71 | fmt.Println("create ca failed", err) 72 | } 73 | return ca_b, priv 74 | } 75 | 76 | func genChildCert(cert tls.Certificate, ips, names []string) []byte { 77 | 78 | parent, err := x509.ParseCertificate(cert.Certificate[0]) 79 | 80 | if err != nil { 81 | fmt.Println("create child cert failed") 82 | return nil 83 | } 84 | 85 | s, _ := rand.Prime(rand.Reader, 128) 86 | 87 | template := &x509.Certificate{ 88 | SerialNumber: s, 89 | Subject: pkix.Name{Organization: []string{"Argo Incorporated"}}, 90 | Issuer: pkix.Name{Organization: []string{"Argo Incorporated"}}, 91 | NotBefore: time.Now(), 92 | NotAfter: time.Now().AddDate(10, 0, 0), 93 | BasicConstraintsValid: true, 94 | IsCA: false, 95 | ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, 96 | KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, 97 | } 98 | if ips != nil { 99 | is := make([]net.IP, 0) 100 | for _, i := range ips { 101 | is = append(is, net.ParseIP(i)) 102 | } 103 | template.IPAddresses = is 104 | } 105 | if names != nil { 106 | template.DNSNames = names 107 | } 108 | 109 | private := cert.PrivateKey.(*rsa.PrivateKey) 110 | 111 | certP, _ := x509.ParseCertificate(cert.Certificate[0]) 112 | public := certP.PublicKey.(*rsa.PublicKey) 113 | 114 | cab, err := x509.CreateCertificate(rand.Reader, template, parent, public, private) 115 | if err != nil { 116 | fmt.Println("create ca failed", err) 117 | os.Exit(1) 118 | } 119 | 120 | fmt.Println("[*] Child Certificate files generated") 121 | return cab 122 | } 123 | 124 | func dumpData(r io.Reader, source string, id int) { 125 | 126 | if config.Raw && config.ToFile != "" { 127 | io.Copy(sessionFile, r) 128 | } else { 129 | 130 | var fw *bufio.Writer // used to write to file 131 | var outDumper io.WriteCloser // used to write hex dump to file 132 | if config.ToFile != "" { 133 | fw = bufio.NewWriter(sessionFile) 134 | if !config.Raw { 135 | outDumper = hex.Dumper(fw) 136 | defer outDumper.Close() 137 | } 138 | } 139 | 140 | data := make([]byte, 512) 141 | for { 142 | n, err := r.Read(data) 143 | if n > 0 { 144 | // hex.Dump + screen output slows things down badly, up to a 5x slow-down 145 | // best to dump to file and view with tail -f 146 | // best yet is to only view the file after the transfer completes 147 | if !config.Raw { 148 | if config.ToFile != "" { 149 | fw.WriteString(fmt.Sprintf("From %s [%d]:\n", source, id)) 150 | // doing this is using hex.Dumper(fw) is slightly faster than 151 | // using `fw.WriteString(hex.Dump(data[:n]))` 152 | // even though the code is debatable uglier 153 | if config.RichRaw { // don't hex dump, this is basically enriched raw 154 | fw.Write(data[:n]) 155 | } else { 156 | outDumper.Write(data[:n]) 157 | } 158 | fw.WriteByte('\n') 159 | fw.Flush() 160 | } else { 161 | fmt.Printf("From %s [%d]:\n", source, id) 162 | fmt.Println(hex.Dump(data[:n])) 163 | } 164 | } 165 | } 166 | if err != nil && err != io.EOF { 167 | fmt.Printf("unable to read data %v", err) 168 | break 169 | } 170 | if n == 0 { 171 | break 172 | } 173 | } 174 | } 175 | 176 | } 177 | 178 | func handleServerMessage(connR, connL net.Conn, id int, closer *sync.Once) { 179 | // see comments in handleConnection 180 | // this is the same, just inverse, reads from server, writes to client 181 | closeFunc := func() { 182 | fmt.Println("[*] Connections closed.") 183 | _ = connL.Close() 184 | _ = connR.Close() 185 | } 186 | 187 | r, w := io.Pipe() 188 | tee := io.MultiWriter(connL, w) 189 | go dumpData(r, "SERVER", id) 190 | _, e := io.Copy(tee, connR) 191 | 192 | if e != nil && e != io.EOF { 193 | // check if error is about the closed connection 194 | // this is expected in most cases, so don't make a noise about it 195 | netOpError, ok := e.(*net.OpError) 196 | if ok && netOpError.Err.Error() != "use of closed network connection" { 197 | fmt.Printf("bad io.Copy [handleServerMessage]: %v", e) 198 | } 199 | } 200 | 201 | // ensure connections are closed. With the sync, this will either happen here 202 | // or in the handleConnection function 203 | closer.Do(closeFunc) 204 | } 205 | 206 | func handleConnection(connL net.Conn, isTLS bool) { 207 | var err error 208 | var connR net.Conn 209 | var closer sync.Once 210 | 211 | // make sure connections get closed 212 | closeFunc := func() { 213 | fmt.Println("[*] Connections closed") 214 | _ = connL.Close() 215 | _ = connR.Close() 216 | } 217 | 218 | if isTLS { 219 | conf := tls.Config{InsecureSkipVerify: true} 220 | 221 | if config.ClientKeyFile != "" { //use mtls 222 | cert, err := tls.LoadX509KeyPair(config.ClientCertFile, config.ClientKeyFile) 223 | if err != nil { 224 | fmt.Printf("couldn't load cert, %v", err) 225 | return 226 | } 227 | conf.Certificates = []tls.Certificate{cert} 228 | } 229 | 230 | connR, err = tls.Dial("tcp", config.Remotehost, &conf) 231 | } else { 232 | connR, err = net.Dial("tcp", config.Remotehost) 233 | } 234 | 235 | if err != nil { 236 | fmt.Printf("[x] Couldn't connect: %v", err) 237 | return 238 | } 239 | 240 | fmt.Printf("[*][%d] Connected to server: %s\n", ids, connR.RemoteAddr()) 241 | 242 | // setup handler to read from server and print to screen 243 | go handleServerMessage(connR, connL, ids, &closer) 244 | 245 | // setup a pipe that will allow writing to the output (stdout) writer, without 246 | // consuming the data 247 | r, w := io.Pipe() 248 | 249 | // create a MultiWriter which allows writing to multiple writers at once. 250 | // this means each read from the client, will result in a write to both the server writer and the pipe writer, 251 | // which then gets sent to the "dumpData" reader, which will output it to the screen 252 | // directly pass connR (server) into the multiwriter. There is no need to allocate a new io.Writer(connR) 253 | tee := io.MultiWriter(connR, w) 254 | 255 | // background the dumping of data to screen 256 | go dumpData(r, "CLIENT", ids) 257 | ids++ 258 | 259 | // consume all data and forward between connections in memory 260 | // directly pass connL (client) into the io.Copy as the reader. There is no need to create a new io.Reader(connL) 261 | _, e := io.Copy(tee, connL) 262 | if e != nil && e != io.EOF { 263 | fmt.Printf("bad io.Copy [handleConnection]: %v", e) 264 | } 265 | 266 | // ensure connections are closed. With the sync, this will either happen here 267 | // or in the handleServerMessage function 268 | closer.Do(closeFunc) 269 | 270 | } 271 | 272 | func startListener(isTLS bool) { 273 | 274 | conn, err := net.Listen("tcp", fmt.Sprint(config.Localhost, ":", config.Localport)) 275 | if err != nil { 276 | panic("failed to start listener: " + err.Error()) 277 | } 278 | 279 | if isTLS { 280 | var cert tls.Certificate 281 | if config.CACertFile != "" { 282 | cert, _ = tls.LoadX509KeyPair(config.CACertFile, config.CAKeyFile) 283 | } else { 284 | fmt.Println("[*] Generating cert") 285 | cab, priv := genCert() 286 | cert = tls.Certificate{ 287 | Certificate: [][]byte{cab}, 288 | PrivateKey: priv, 289 | } 290 | } 291 | 292 | if config.IPS != nil || config.Names != nil { 293 | newCert := genChildCert(cert, config.IPS, config.Names) 294 | cert.Certificate = [][]byte{newCert} 295 | } 296 | 297 | // we don't have to set mTLS on the listener, it will simply accept connection with or 298 | // without the client supplying a cert. The mTLS part happens with the connection to the 299 | // upstream host 300 | conf := tls.Config{ 301 | Certificates: []tls.Certificate{cert}, 302 | } 303 | 304 | // optional to add mTLS on the listener side 305 | if config.ListenerMTLS && config.ClientKeyFile != "" { 306 | caCert, err := os.ReadFile(config.ClientKeyFile) 307 | if err != nil { 308 | panic("failed to start listener: " + err.Error()) 309 | } 310 | caCertPool := x509.NewCertPool() 311 | caCertPool.AppendCertsFromPEM(caCert) 312 | conf.ClientCAs = caCertPool 313 | conf.ClientAuth = tls.RequireAndVerifyClientCert 314 | } 315 | 316 | conf.Rand = rand.Reader 317 | // wrap conn into a TLS listener 318 | conn = tls.NewListener(conn, &conf) 319 | } 320 | 321 | fmt.Println("[*] Listening...") 322 | defer conn.Close() 323 | 324 | for { 325 | cl, err := conn.Accept() 326 | if err != nil { 327 | fmt.Printf("server: accept: %v", err) 328 | break 329 | } 330 | fmt.Printf("[*] Accepted from: %s\n", cl.RemoteAddr()) 331 | go handleConnection(cl, isTLS) 332 | } 333 | } 334 | 335 | func setConfig(configFile string, localPort int, localHost, remoteHost string, caCertFile, caKeyFile string, clientCertFile, clientKeyFile, outFile string) { 336 | if configFile != "" { 337 | data, err := os.ReadFile(configFile) 338 | if err != nil { 339 | fmt.Println("[-] Not a valid config file: ", err) 340 | os.Exit(1) 341 | } 342 | err = json.Unmarshal(data, &config) 343 | if err != nil { 344 | fmt.Println("[-] Not a valid config file: ", err) 345 | os.Exit(1) 346 | } 347 | } else { 348 | config = Config{TLS: &TLS{}} 349 | } 350 | 351 | if caCertFile != "" { 352 | config.CACertFile = caCertFile 353 | config.CAKeyFile = caKeyFile 354 | } 355 | 356 | if clientCertFile != "" { 357 | config.ClientCertFile = clientCertFile 358 | config.ClientKeyFile = clientKeyFile 359 | } 360 | 361 | if localPort != 0 { 362 | config.Localport = localPort 363 | } 364 | if localHost != "" { 365 | config.Localhost = localHost 366 | } 367 | if remoteHost != "" { 368 | config.Remotehost = remoteHost 369 | } 370 | 371 | if outFile != "" { 372 | config.ToFile = outFile 373 | } 374 | } 375 | 376 | func main() { 377 | localPort := flag.Int("p", 0, "Local Port to listen on") 378 | localHost := flag.String("l", "", "Local address to listen on") 379 | remoteHostPtr := flag.String("r", "", "Remote Server address host:port") 380 | configPtr := flag.String("c", "", "Use a config file (set TLS ect) - Commandline params overwrite config file") 381 | tlsPtr := flag.Bool("s", false, "Create a TLS Proxy") 382 | listenerMTLSPtr := flag.Bool("lmtls", false, "Enable mTLS on the listener. Requires clientKey") 383 | caCertFilePtr := flag.String("cert", "", "Use a specific ca cert file") 384 | caKeyFilePtr := flag.String("key", "", "Use a specific ca key file (must be set if --cert is set") 385 | clientCertPtr := flag.String("clientCert", "", "A public client cert to use for mTLS") 386 | clientKeyPtr := flag.String("clientKey", "", "A public client key to use for mTLS") 387 | quietPtr := flag.Bool("q", false, "Hide app messages and just show the data stream") 388 | rawPtr := flag.Bool("raw", false, "Don't use hex.dump to pretty format output") 389 | richRawPtr := flag.Bool("richraw", false, "Slightly enrich the raw output, don't use hex.dump to pretty format output") 390 | outFilePtr := flag.String("o", "", "Write output to file") 391 | 392 | flag.Parse() 393 | 394 | if *caCertFilePtr != "" && *caKeyFilePtr == "" { 395 | fmt.Println("[x] -key is required when -cert is set") 396 | os.Exit(1) 397 | } 398 | 399 | if *clientCertPtr != "" && *clientKeyPtr == "" { 400 | fmt.Println("[x] -clientKey is required when -clientCert is set") 401 | os.Exit(1) 402 | } 403 | 404 | setConfig(*configPtr, *localPort, *localHost, *remoteHostPtr, *caCertFilePtr, *caKeyFilePtr, *clientCertPtr, *clientKeyPtr, *outFilePtr) 405 | 406 | config.ListenerMTLS = *listenerMTLSPtr 407 | if config.ListenerMTLS { 408 | fmt.Println("[-] ClientCertFile must be set when using listener mTLS") 409 | os.Exit(1) 410 | } 411 | config.Quiet = *quietPtr 412 | config.Raw = *rawPtr 413 | config.RichRaw = *richRawPtr 414 | 415 | if config.Raw && config.RichRaw { 416 | fmt.Println("[-] Conflicting configuration, -raw and -richraw can't be used together.") 417 | os.Exit(1) 418 | } 419 | 420 | if config.Raw && config.ToFile == "" { 421 | fmt.Println("[-] Raw mode specified but no output file supplied. There won't be any output!") 422 | } 423 | 424 | if config.ToFile != "" { 425 | var e error 426 | sessionFile, e = os.Create(config.ToFile) 427 | if e != nil { 428 | fmt.Println("[x] Couldn't open file for writing") 429 | os.Exit(1) 430 | } 431 | defer sessionFile.Close() 432 | } 433 | 434 | if config.Remotehost == "" { 435 | fmt.Println("[x] Remote host required") 436 | flag.PrintDefaults() 437 | os.Exit(1) 438 | } 439 | 440 | startListener(*tlsPtr) 441 | } 442 | --------------------------------------------------------------------------------