2

I am attempting to write/print text to the screen from a Go program launched from another console/terminal application--a "door" program that launches from an old-school Bulletin Board System (BBS).

The BBS itself runs over a telnet connection, localhost:2323. And when launching my program, the BBS automatically adds the correct socket handle as an argument, which I can then read using Flag (it's an integer, like 236).

Obviously, in Linux, I'd just use fmt.Println("Hello World!") using os.Stdout... But on Windows, I need to somehow pipe/redirect the Go program's output to the provided socket.

Here's the function I started with:

func writeOut(fd int, buf []byte) bool {
    for len(buf) > 0 {
        n, err := syscall.Write(syscall.Handle(fd), buf)
        if err != nil {
            fmt.Println(err)
            return false
        }
        buf = buf[n:]
    }
    return true
}

called from:

 writeOut(socketInt, []byte("Writing to Windows socket..."))

The error returned is: The parameter is incorrect

What am I doing wrong, and how would this be accomplished in Go?

Robbie
  • 447
  • 1
  • 6
  • 19
  • 1
    You're not checking possible error from `syscall.Write`. Quite possibly, I'd tell you what's the problem is. – kostix Feb 25 '22 at 18:44
  • Ah, yes. Updated. Error: "The parameter is incorrect." – Robbie Feb 25 '22 at 18:54
  • [Works for me](https://gist.github.com/kostix/87b94e938cf1c35a660e21bc5424f4c3). The code accepts a client TCP connection, retrieves its OS handle, marks it as inherited then runs a "door" process passing it the integer value of the handle on the command line, and then the "door process" uses that handle to write "hello\r\n" to the client. – kostix Feb 25 '22 at 19:35
  • 2
    By the way note that I/O functions can leigtimately return an error after having had written or read something. That's not your case, but otherwise you might want to check for `n != 0` and do something if the test passes before checking for an error. – kostix Feb 25 '22 at 19:40

1 Answers1

2

You can't arbitrarily pass file or socket handles to another process that isn't inheriting them from your process in the first place. Each process has its own unique set of handles. In POSIX inheriting socket handles is possible (albeit not recommended) but in Windows they simply cannot be inherited (see Are TCP SOCKET handles inheritable?).

You can redirect stdout to a TCP socket when calling CreateProcess though, so that when invoked, your program can indeed fmt.Println to stdout and the output would go straight to the socket:

func serveDoor(conn *net.TCPConn, name string, args ...string) {
    defer conn.Close()

    cmd := exec.Command(name, args...)
    cmd.Stdin = conn
    cmd.Stdout = conn
    cmd.Stderr = conn

    err := cmd.Run()
    fmt.Println("door finished:", err)
}

(full gist)

Another solution is to use a pipe and pump it to the socket.

rustyx
  • 80,671
  • 25
  • 200
  • 267
  • Any idea how might this be done in Go? Thanks! – Robbie Feb 25 '22 at 23:35
  • @Robbie, but that's exactly what is being done in my gist which presents a complete working example. Have you bothered to read it? – kostix Feb 26 '22 at 22:42
  • @kostix - i did, and didn't work for me under Win 64. Ran that code exactly as posted in the GIST... I wonder if you are running via WINE if this makes a difference? Also, trying to figure out if rustyx's answer means that inheritance isn't possible, hence the continued error I'm getting. Additionally, the BBS is already handling the initials client TCP connection, so I have no control over this. – Robbie Feb 27 '22 at 01:43
  • @Robbie, Sockets definitely work under wine—verified that on multiple occasions ;) The code explicitly uses inheriting via win32 API call. Does the code print any errors when it's running? – kostix Feb 27 '22 at 10:10
  • @Robbie, see my update. There is no need for any "door" program, the server can run the client directly *and* redirect its output to a socket. As for kostix's gist - it may work in Wine alright, but would never work under real Windows. – rustyx Feb 27 '22 at 11:18
  • @rustyx - I only have control over the 'door' program. The server itself, which launces the door, is another program out of my control. Does that make sense? Trying to figure out how to parse your example with that context. – Robbie Mar 03 '22 at 19:12
  • Well the communication you've described between the server and the door program is a technical impossibility on Windows. Unless there's more to it than you describe, I see no option to fix it without changing the server. – rustyx Mar 04 '22 at 09:36
  • I was thinking that as well, but there are C++ libraries that do this (https://github.com/SynchronetBBS/sbbs/tree/master/src/odoors), so I know it's possible. I probably need to experiment a bit more. – Robbie Mar 04 '22 at 15:48