Skip to content

Instantly share code, notes, and snippets.

@ancms2600
Created June 23, 2020 01:42
Show Gist options
  • Save ancms2600/8676a135ad0ad143d596a097cc85bf34 to your computer and use it in GitHub Desktop.
Save ancms2600/8676a135ad0ad143d596a097cc85bf34 to your computer and use it in GitHub Desktop.
Listening service, upon tcp connection, launches a process (once per connection) and pipes stdin/out/err back and forth. Used to make hosting simple netcat/telnet services easier (ie. during CTF competitions)
package main
import (
"bufio"
"fmt"
"net"
"os"
"io"
"os/exec"
"time"
)
const (
CONN_HOST = "0.0.0.0"
CONN_PORT = "3000"
CONN_TYPE = "tcp"
)
func main() {
l, err := net.Listen(CONN_TYPE, CONN_HOST+":"+CONN_PORT)
if err != nil {
fmt.Printf("%s Error listening: %s\n", ts(), err.Error())
os.Exit(1)
}
defer l.Close()
process := os.Args[1]
fmt.Printf("%s Will run process: %s\n", ts(), process)
fmt.Printf("%s Listening on %s:%s\n", ts(), CONN_HOST, CONN_PORT)
for {
conn, err := l.Accept()
if err != nil {
fmt.Printf("%s Error accepting: %s\n", ts(), err.Error())
os.Exit(1)
}
go handleRequest(conn, process)
}
}
func ts() string {
return time.Now().Format(time.RFC3339)
}
func handleRequest(conn net.Conn, process string) {
fmt.Printf("%s Client %s starting new process.\n", ts(), conn.RemoteAddr())
serverCmd := exec.Command(process)
serverIn, err := serverCmd.StdinPipe()
if err != nil {
fmt.Printf("%s Error streaming process stdin: %s\n", ts(), err.Error())
}
serverOut, err := serverCmd.StdoutPipe()
if err != nil {
fmt.Printf("%s Error streaming process stdout: %s\n", ts(), err.Error())
}
serverErr, err := serverCmd.StderrPipe()
if err != nil {
fmt.Printf("%s Error streaming process stderr: %s\n", ts(), err.Error())
}
err2 := serverCmd.Start()
if err2 != nil {
fmt.Printf("%s Error starting process: %s\n", ts(), err2.Error())
}
go streamIn(conn, serverCmd, serverIn)
go streamOut(conn, serverCmd, serverOut)
go streamErr(conn, serverCmd, serverErr)
go timeout(conn, serverCmd);
serverCmd.Wait()
// allow time for stdout to flush over socket
time.Sleep(1 * time.Second)
serverIn.Close()
close(conn, serverCmd, "hangup");
}
func timeout(conn net.Conn, cmd *exec.Cmd) {
time.Sleep(2 * time.Minute)
close(conn, cmd, "timeout");
}
func close(conn net.Conn, cmd *exec.Cmd, reason string) {
err1 := conn.Close()
if err1 != nil {
// fmt.Printf("%s Error failed to kill process: %s\n", ts(), err.Error())
} else {
fmt.Printf("%s Client %s socket closed by service. reason: %s\n", ts(), conn.RemoteAddr(), reason)
}
err := cmd.Process.Kill()
if err != nil {
// fmt.Printf("%s Error failed to kill process: %s\n", ts(), err.Error())
} else {
fmt.Printf("%s Client %s process killed by service. reason: %s\n", ts(), conn.RemoteAddr(), reason)
}
}
func streamIn(conn net.Conn, cmd *exec.Cmd, serverIn io.WriteCloser) {
for {
buf := make([]byte, 1024)
_, err := conn.Read(buf)
if err != nil {
//fmt.Printf("%s Error reading user input from socket: %s\n", ts(), err.Error())
break;
}
serverIn.Write(buf)
}
}
func streamOut(conn net.Conn, cmd *exec.Cmd, serverOut io.ReadCloser) {
scanner := bufio.NewScanner(serverOut)
scanner.Split(bufio.ScanBytes)
for scanner.Scan() {
m := scanner.Bytes()
conn.Write(m)
}
}
func streamErr(conn net.Conn, cmd *exec.Cmd, serverErr io.ReadCloser) {
scanner := bufio.NewScanner(serverErr)
scanner.Split(bufio.ScanBytes)
for scanner.Scan() {
m := scanner.Bytes()
conn.Write(m)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment