mirror of
https://github.com/tale/headplane.git
synced 2026-08-10 14:06:51 +00:00
chore: reorganize go code
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
package sshutil
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// An SSH frame is used to wrap raw binary data to and from an SSH session
|
||||
// in order to allow multiplexing connections over a single file descriptor.
|
||||
//
|
||||
// In practice, this is how we can easily support multiple SSH connections
|
||||
// through the 2 file descriptors created by the parent node process.
|
||||
//
|
||||
// This is the format of an SSH frame:
|
||||
// - Magic: The first 4 bytes are HPLS (0x48504C53) to identify the frame.
|
||||
// - Version Byte: The first byte is the version of the frame format.
|
||||
// - Channel Type: The second byte indicates the type of channel.
|
||||
// - Session ID: The next bytes are the length and actual session ID.
|
||||
// - Payload: The remaining bytes are the payload length and actual data.
|
||||
//
|
||||
// +---------+----------+--------------+-------------+----------+
|
||||
// | Magic | Version | Channel Type | SID Length | SID |
|
||||
// | 4 bytes | 1 byte | 1 byte | 1 byte (S) | S bytes |
|
||||
// +---------+----------+--------------+------------------------+
|
||||
// | Payload Length | Payload |
|
||||
// | 4 bytes (u32, P) | P bytes |
|
||||
// +--------------------+---------------------------------------+
|
||||
|
||||
const (
|
||||
MagicString = "HPLS"
|
||||
VersionByte = 1
|
||||
)
|
||||
|
||||
type ChannelType int
|
||||
|
||||
const (
|
||||
ChannelTypeStdin ChannelType = iota
|
||||
ChannelTypeStdout
|
||||
ChannelTypeStderr
|
||||
)
|
||||
|
||||
type SSHFrame struct {
|
||||
ChannelType ChannelType
|
||||
SessionID string
|
||||
Payload []byte
|
||||
|
||||
Length func() int
|
||||
}
|
||||
|
||||
type HPLSFrame1 struct{}
|
||||
|
||||
func (t HPLSFrame1) Encode(frame SSHFrame) ([]byte, error) {
|
||||
frameChan := frame.ChannelType
|
||||
switch frameChan {
|
||||
case ChannelTypeStdin, ChannelTypeStdout, ChannelTypeStderr:
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid channel type: %d", frameChan)
|
||||
}
|
||||
|
||||
if len(frame.SessionID) == 0 {
|
||||
return nil, fmt.Errorf("session ID cannot be empty")
|
||||
}
|
||||
|
||||
if len(frame.Payload) == 0 {
|
||||
return nil, fmt.Errorf("payload cannot be empty")
|
||||
}
|
||||
|
||||
sid := []byte(frame.SessionID)
|
||||
if len(sid) > 255 {
|
||||
return nil, fmt.Errorf("session ID exceeds 255 byte limit")
|
||||
}
|
||||
|
||||
if len(frame.Payload) > 0xFFFFFFFF {
|
||||
return nil, fmt.Errorf("payload exceeds 4GB limit")
|
||||
}
|
||||
|
||||
frameLen := 4 // Magic
|
||||
frameLen += 1 // Version byte
|
||||
frameLen += 1 // Channel type
|
||||
frameLen += 1 + len(sid) // Session ID length + SID
|
||||
frameLen += 4 + len(frame.Payload) // Payload length + Payload
|
||||
|
||||
buf := make([]byte, frameLen)
|
||||
copy(buf[0:4], []byte(MagicString))
|
||||
buf[4] = VersionByte
|
||||
buf[5] = byte(frameChan)
|
||||
buf[6] = byte(len(sid))
|
||||
|
||||
offset := 7 + len(sid)
|
||||
copy(buf[7:offset], sid)
|
||||
|
||||
binary.BigEndian.PutUint32(buf[offset:offset+4], uint32(len(frame.Payload)))
|
||||
copy(buf[offset+4:], frame.Payload)
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (t HPLSFrame1) Decode(buf []byte) (SSHFrame, error) {
|
||||
frame := SSHFrame{}
|
||||
if len(buf) < 5 || string(buf[0:4]) != MagicString || buf[4] != VersionByte {
|
||||
return frame, fmt.Errorf("illegal HPLS1 frame format")
|
||||
}
|
||||
|
||||
frame.ChannelType = ChannelType(buf[5])
|
||||
if frame.ChannelType < ChannelTypeStdin || frame.ChannelType > ChannelTypeStderr {
|
||||
return frame, fmt.Errorf("invalid channel type: %d", frame.ChannelType)
|
||||
}
|
||||
|
||||
sidLen := int(buf[6])
|
||||
if len(buf) < 7+sidLen+4 {
|
||||
return frame, fmt.Errorf("buffer too short for session ID and payload length")
|
||||
}
|
||||
|
||||
frame.SessionID = string(buf[7 : 7+sidLen])
|
||||
payloadLen := int(binary.BigEndian.Uint32(buf[7+sidLen:]))
|
||||
if len(buf) < 7+sidLen+4+payloadLen {
|
||||
return frame, fmt.Errorf("buffer too short for payload")
|
||||
}
|
||||
|
||||
frame.Payload = buf[7+sidLen+4 : 7+sidLen+4+payloadLen]
|
||||
frame.Length = func() int {
|
||||
return 7 + sidLen + 4 + payloadLen
|
||||
}
|
||||
|
||||
return frame, nil
|
||||
}
|
||||
Reference in New Issue
Block a user