Update Go module path and all import references to reflect the migration from Gitea (git.t-juice.club) to Forgejo (code.t-juice.club). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
92 lines
2.0 KiB
Go
92 lines
2.0 KiB
Go
package shell
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
|
|
"code.t-juice.club/torjus/oubliette/internal/storage"
|
|
)
|
|
|
|
// Shell is the interface that all honeypot shell implementations must satisfy.
|
|
type Shell interface {
|
|
Name() string
|
|
Description() string
|
|
Handle(ctx context.Context, sess *SessionContext, rw io.ReadWriteCloser) error
|
|
}
|
|
|
|
// SessionContext carries metadata about the current SSH session.
|
|
type SessionContext struct {
|
|
SessionID string
|
|
Username string
|
|
RemoteAddr string
|
|
ClientVersion string
|
|
Store storage.Store
|
|
ShellConfig map[string]any
|
|
CommonConfig ShellCommonConfig
|
|
OnCommand func(shell string) // called when a command is executed; may be nil
|
|
}
|
|
|
|
// ShellCommonConfig holds settings shared across all shell types.
|
|
type ShellCommonConfig struct {
|
|
Hostname string
|
|
Banner string
|
|
FakeUser string // override username in prompt; empty = use authenticated user
|
|
}
|
|
|
|
// ReadLine reads a line of input byte-by-byte, handling backspace, Ctrl+C, and Ctrl+D.
|
|
func ReadLine(ctx context.Context, rw io.ReadWriter) (string, error) {
|
|
var buf []byte
|
|
b := make([]byte, 1)
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return "", ctx.Err()
|
|
default:
|
|
}
|
|
|
|
n, err := rw.Read(b)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if n == 0 {
|
|
continue
|
|
}
|
|
|
|
ch := b[0]
|
|
switch {
|
|
case ch == '\r' || ch == '\n':
|
|
fmt.Fprint(rw, "\r\n")
|
|
return string(buf), nil
|
|
|
|
case ch == 4: // Ctrl+D
|
|
if len(buf) == 0 {
|
|
return "", io.EOF
|
|
}
|
|
|
|
case ch == 3: // Ctrl+C
|
|
fmt.Fprint(rw, "^C\r\n")
|
|
return "", nil
|
|
|
|
case ch == 127 || ch == 8: // DEL or Backspace
|
|
if len(buf) > 0 {
|
|
buf = buf[:len(buf)-1]
|
|
fmt.Fprint(rw, "\b \b")
|
|
}
|
|
|
|
case ch == 27: // ESC - start of escape sequence
|
|
// Read and discard the rest of the escape sequence.
|
|
// Most are 3 bytes: ESC [ X (arrow keys, etc.)
|
|
next := make([]byte, 1)
|
|
if n, _ := rw.Read(next); n > 0 && next[0] == '[' {
|
|
rw.Read(next) // read the final byte
|
|
}
|
|
|
|
case ch >= 32 && ch < 127: // printable ASCII
|
|
buf = append(buf, ch)
|
|
rw.Write([]byte{ch})
|
|
}
|
|
}
|
|
}
|