package main import ( "flag" "fmt" "log" "net" "os" "os/exec" "strconv" "strings" "time" ) const ( connHost = "0.0.0.0" connPort = "8080" connType = "tcp" ) func main() { var port int var drain int var cp437 bool flag.IntVar(&port, "p", 0, "Port number to listen on") flag.IntVar(&drain, "d", 2, "Drain seconds") flag.BoolVar(&cp437, "c", false, "Force CP437 translation") flag.Parse() if port == 0 && flag.NArg() != 1 { fmt.Println("I need a Port and a commandline to execute.") flag.PrintDefaults() os.Exit(2) } fmt.Println("Starting " + connType + " server on " + connHost + ":" + strconv.Itoa(port)) l, err := net.Listen("tcp", "0.0.0.0:"+strconv.Itoa(port)) if err != nil { log.Println("Error listening:", err) os.Exit(1) } defer l.Close() for { var c net.Conn var err error c, err = l.Accept() if err != nil { log.Println("Error connecting:", err) return } go Connection(c, drain, flag.Arg(0), cp437) } } func Conn_to_File(conn net.Conn) *os.File { var tcpconn *net.TCPConn = conn.(*net.TCPConn) // This creates a duplicate fd, but once closed -- the fd gets reused! var conn_file *os.File // var err error conn_file, _ = tcpconn.File() return conn_file } func ReadFrom_WriteTo(read net.Conn, write net.Conn, closed *bool) { var buff []byte = make([]byte, 128) var n int var err error defer func() { read.Close() write.Close() if !*closed { log.Println("*Closed*") *closed = true } }() for { n, err = read.Read(buff) if err != nil { return } n, err = write.Write(buff[:n]) if err != nil { return } } } func ReadFrom_WriteToCP437(read net.Conn, write net.Conn, closed *bool) { var buff []byte = make([]byte, 128) var n int var err error defer func() { read.Close() write.Close() if !*closed { log.Println("*Closed*") *closed = true } }() for { n, err = read.Read(buff) if err != nil { return } var line = string(buff[:n]) // This does convert everything to unicode // syncterm doesn't like it (because it doesn't understand // unicode!) // The "door" detects CP437 (not unicode) // This would allow a CP437 door to run as unicode. n, err = write.Write([]byte(CP437_to_Unicode(line))) if err != nil { return } } } func StartProxy(live net.Conn, monitor net.Conn, closed *bool, cp437 bool) { go ReadFrom_WriteTo(live, monitor, closed) if cp437 { go ReadFrom_WriteToCP437(monitor, live, closed) } else { go ReadFrom_WriteTo(monitor, live, closed) } } /* Read from live, write to server. Read from server, write to live. */ func setup_monitor(live net.Conn, closed *bool, cp437 bool) (monitor net.Conn) { var err error var tempsock net.Listener tempsock, err = net.Listen("tcp", "127.0.0.1:0") if err != nil { panic(err) } // I only need address for making the connection. // Get address of listening socket var address string address = tempsock.Addr().String() monitor, err = net.Dial("tcp", address) if err != nil { panic(err) } var server net.Conn server, err = tempsock.Accept() if err != nil { panic(err) } tempsock.Close() *closed = false // monitor established - forward live <-> monitor go StartProxy(live, server, closed, cp437) return monitor } func Connection(conn net.Conn, drain int, cmd string, cp437 bool) { log.Println("Client " + conn.RemoteAddr().String() + " connected.") // Configure telnet connection to work // local echo off, handle CRNL. conn.Write([]byte("\xff\xfb\x01\xff\xfb\x03\xff\xfd\x10")) Drain(conn, drain) var closed bool var proxy net.Conn proxy = setup_monitor(conn, &closed, cp437) // Write out dropfile var conn_file *os.File = Conn_to_File(proxy) var handle int = 3 var err error var fp *os.File fp, err = os.Create("door32.sys") if err != nil { log.Println("os.Create:", err) return } fmt.Fprintf(fp, "2\n%d\n38400\nFake Door32\n1\nBugz Laundry\nBugz\n100\n120\n1\n1\n", handle) fp.Close() var parts []string = strings.Split(cmd, " ") var Exe *exec.Cmd = exec.Command(parts[0], parts[1:]...) Exe.ExtraFiles = make([]*os.File, 1) Exe.ExtraFiles[0] = conn_file Exe.Stderr = os.Stderr Exe.Stdout = os.Stdout err = Exe.Start() if err != nil { log.Println("exec.Cmd.Start():", err) return } log.Println("Door running..." + conn.RemoteAddr().String()) // Add a timeout here - to make sure the door isn't hung. // Is there a way to detect if the conn is disconnected? err = Exe.Wait() if err != nil { log.Println("exec.Cmd.Wait():", err) return } if closed { log.Println("Closed!") } log.Println("Door ended..." + conn.RemoteAddr().String()) conn_file.Close() proxy.Close() conn.Write([]byte("\r\nWelcome back...\r\n")) conn.Close() } func Drain(conn net.Conn, drain int) { conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(drain))) var buff []byte = make([]byte, 32) var n int var err error n, err = conn.Read(buff) if n > 0 { log.Printf("Drained %d bytes [%#v].\n", n, buff[:n]) } if err != nil { log.Println("Drain:", err) } conn.SetReadDeadline(time.Time{}) }