Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to wait for Ctrl+C (session.Signals does not work as expected) #226

Open
MexHigh opened this issue Jan 18, 2024 · 2 comments
Open

How to wait for Ctrl+C (session.Signals does not work as expected) #226

MexHigh opened this issue Jan 18, 2024 · 2 comments

Comments

@MexHigh
Copy link

MexHigh commented Jan 18, 2024

I'm trying to block the handler whenever a reverse port forwarding was requested. The tunnel should exist as long as the user stops the SSH session by entering the Ctrl+C sequence. There is a Signals function that I would expect to do this, but nothing arrives at the channel in my testing code:

func handler(s ssh.Session) {
    // ...
    if requestedRevPortForwarding { // set in the reversePortForwardingHandler using ctx.SetValue()
        io.WriteString(s, "[*] Forwarding initialized!\n")
	io.WriteString(s, "[*] Stop with Ctrl+C")

	signals := make(chan ssh.Signal, 10)
	s.Signals(signals)
	go func() {
            for {
	        fmt.Println(<-signals)
	    }
        }()
    }
    <-s.Context().Done() // block
    // ...
}

With this example the user can only exit the session by closing the terminal he's in. There must be a way to handle this with signals. I know that I can use terminals to get like a "quit" command, but I want to use the Ctrl+C approach here.

Any idea why this is not working or an alternative to solve this?

@hugorosario
Copy link

hugorosario commented Feb 13, 2024

I have also been playing around with this project and I believe that signals are not yet fully implemented.
I managed to work around this by using the "golang.org/x/term" package to handle the session Read/Writer which in fact will handle CTRL+C correctly.

Example:

Handler: ssh.Handler(func(s ssh.Session) {
	io.WriteString(s, "Shell is ready.\nPress CTRL+C to terminate session.\n")
	shell := term.NewTerminal(s, "")
	shell.SetPrompt(string("> " + string(shell.Escape.Reset)))
	for {
		line, err := shell.ReadLine()
		if err == io.EOF {
			return
		}
		if err != nil {
			io.WriteString(s, fmt.Sprintln(err))
			continue
		}
		if line == "" {
			continue
		}
		io.WriteString(s, line)
	}
}),

@ChaoticByte
Copy link

ChaoticByte commented Jun 28, 2024

I found an easier solution (that also supports Ctrl+D):

const KeyCtrlC = 3 // ASCII ETX -> Ctrl+C
const KeyCtrlD = 4 // ASCII EOT -> Ctrl+D

// ...

func HandleSession(session ssh.Session) {
	// Hello there
	// ...
	for {
		// we read one byte at once
		c := []byte{0}
		_, err = session.Read(c)
		if err != nil {
			// error handling ...
			break
		}
		// we check if this byte is the ETX or EOT
		// control character & end the loop
		if c[0] == KeyCtrlC || c[0] == KeyCtrlD { break }
	}
	// ...
	// Bye
}

// ...

Actually that's how they do it in golang.org/x/term ^^

EDIT: You could also pass ssh.NoPty() to ssh.ListenAndServe() so that no pseudo-terminal is allocated for a client. Then the SSH client itself handles Ctrl+C and Ctrl+D.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants