feature: Adds basic data command support.

This commit is contained in:
Tera << 8 2025-02-16 15:02:50 -05:00
parent ede4d528aa
commit 17e1491f96
Signed by: imterah
GPG key ID: 8FA7DD57BA6CEA37
14 changed files with 2241 additions and 195 deletions

View file

@ -0,0 +1,310 @@
package backendutil_custom
import (
"fmt"
"net"
"os"
"git.terah.dev/imterah/hermes/backend/backendutil"
"git.terah.dev/imterah/hermes/backend/commonbackend"
"git.terah.dev/imterah/hermes/backend/sshappbackend/datacommands"
"github.com/charmbracelet/log"
)
type BackendApplicationHelper struct {
Backend BackendInterface
SocketPath string
socket net.Conn
}
func (helper *BackendApplicationHelper) Start() error {
log.Debug("BackendApplicationHelper is starting")
err := backendutil.ConfigureProfiling()
if err != nil {
return err
}
log.Debug("Currently waiting for Unix socket connection...")
helper.socket, err = net.Dial("unix", helper.SocketPath)
if err != nil {
return err
}
log.Debug("Sucessfully connected")
for {
commandType, commandRaw, err := datacommands.Unmarshal(helper.socket)
if err != nil && err.Error() != "couldn't match command ID" {
return err
}
switch commandType {
case "proxyConnectionsRequest":
proxyConnectionRequest, ok := commandRaw.(*datacommands.ProxyConnectionsRequest)
if !ok {
return fmt.Errorf("failed to typecast")
}
connections := helper.Backend.GetAllClientConnections(proxyConnectionRequest.ProxyID)
serverParams := &datacommands.ProxyConnectionsResponse{
Type: "proxyConnectionsResponse",
Connections: connections,
}
byteData, err := datacommands.Marshal(serverParams.Type, serverParams)
if err != nil {
return err
}
if _, err = helper.socket.Write(byteData); err != nil {
return err
}
case "removeProxy":
command, ok := commandRaw.(*datacommands.RemoveProxy)
if !ok {
return fmt.Errorf("failed to typecast")
}
ok, err = helper.Backend.StopProxy(command)
var hasAnyFailed bool
if !ok {
log.Warnf("failed to remove proxy (ID %d): RemoveProxy returned into failure state", command.ProxyID)
hasAnyFailed = true
} else if err != nil {
log.Warnf("failed to remove proxy (ID %d): %s", command.ProxyID, err.Error())
hasAnyFailed = true
}
response := &datacommands.ProxyStatusResponse{
Type: "proxyStatusResponse",
ProxyID: command.ProxyID,
IsActive: hasAnyFailed,
}
responseMarshalled, err := commonbackend.Marshal(response.Type, response)
if err != nil {
log.Error("failed to marshal response: %s", err.Error())
continue
}
helper.socket.Write(responseMarshalled)
default:
commandType, commandRaw, err := commonbackend.Unmarshal(helper.socket)
if err != nil {
return err
}
switch commandType {
case "start":
command, ok := commandRaw.(*commonbackend.Start)
if !ok {
return fmt.Errorf("failed to typecast")
}
ok, err = helper.Backend.StartBackend(command.Arguments)
var (
message string
statusCode int
)
if err != nil {
message = err.Error()
statusCode = commonbackend.StatusFailure
} else {
statusCode = commonbackend.StatusSuccess
}
response := &commonbackend.BackendStatusResponse{
Type: "backendStatusResponse",
IsRunning: ok,
StatusCode: statusCode,
Message: message,
}
responseMarshalled, err := commonbackend.Marshal(response.Type, response)
if err != nil {
log.Error("failed to marshal response: %s", err.Error())
continue
}
helper.socket.Write(responseMarshalled)
case "stop":
_, ok := commandRaw.(*commonbackend.Stop)
if !ok {
return fmt.Errorf("failed to typecast")
}
ok, err = helper.Backend.StopBackend()
var (
message string
statusCode int
)
if err != nil {
message = err.Error()
statusCode = commonbackend.StatusFailure
} else {
statusCode = commonbackend.StatusSuccess
}
response := &commonbackend.BackendStatusResponse{
Type: "backendStatusResponse",
IsRunning: !ok,
StatusCode: statusCode,
Message: message,
}
responseMarshalled, err := commonbackend.Marshal(response.Type, response)
if err != nil {
log.Error("failed to marshal response: %s", err.Error())
continue
}
helper.socket.Write(responseMarshalled)
case "backendStatusRequest":
_, ok := commandRaw.(*commonbackend.BackendStatusRequest)
if !ok {
return fmt.Errorf("failed to typecast")
}
ok, err := helper.Backend.GetBackendStatus()
var (
message string
statusCode int
)
if err != nil {
message = err.Error()
statusCode = commonbackend.StatusFailure
} else {
statusCode = commonbackend.StatusSuccess
}
response := &commonbackend.BackendStatusResponse{
Type: "backendStatusResponse",
IsRunning: ok,
StatusCode: statusCode,
Message: message,
}
responseMarshalled, err := commonbackend.Marshal(response.Type, response)
if err != nil {
log.Error("failed to marshal response: %s", err.Error())
continue
}
helper.socket.Write(responseMarshalled)
case "addProxy":
command, ok := commandRaw.(*commonbackend.AddProxy)
if !ok {
return fmt.Errorf("failed to typecast")
}
id, ok, err := helper.Backend.StartProxy(command)
var hasAnyFailed bool
if !ok {
log.Warnf("failed to add proxy (%s:%d -> remote:%d): StartProxy returned into failure state", command.SourceIP, command.SourcePort, command.DestPort)
hasAnyFailed = true
} else if err != nil {
log.Warnf("failed to add proxy (%s:%d -> remote:%d): %s", command.SourceIP, command.SourcePort, command.DestPort, err.Error())
hasAnyFailed = true
}
response := &datacommands.ProxyStatusResponse{
Type: "proxyStatusResponse",
ProxyID: id,
IsActive: !hasAnyFailed,
}
responseMarshalled, err := commonbackend.Marshal(response.Type, response)
if err != nil {
log.Error("failed to marshal response: %s", err.Error())
continue
}
helper.socket.Write(responseMarshalled)
case "checkClientParameters":
command, ok := commandRaw.(*commonbackend.CheckClientParameters)
if !ok {
return fmt.Errorf("failed to typecast")
}
resp := helper.Backend.CheckParametersForConnections(command)
resp.Type = "checkParametersResponse"
resp.InResponseTo = "checkClientParameters"
byteData, err := commonbackend.Marshal(resp.Type, resp)
if err != nil {
return err
}
if _, err = helper.socket.Write(byteData); err != nil {
return err
}
case "checkServerParameters":
command, ok := commandRaw.(*commonbackend.CheckServerParameters)
if !ok {
return fmt.Errorf("failed to typecast")
}
resp := helper.Backend.CheckParametersForBackend(command.Arguments)
resp.Type = "checkParametersResponse"
resp.InResponseTo = "checkServerParameters"
byteData, err := commonbackend.Marshal(resp.Type, resp)
if err != nil {
return err
}
if _, err = helper.socket.Write(byteData); err != nil {
return err
}
default:
log.Warn("Unsupported command recieved: %s", commandType)
}
}
}
}
func NewHelper(backend BackendInterface) *BackendApplicationHelper {
socketPath, ok := os.LookupEnv("HERMES_API_SOCK")
if !ok {
log.Warn("HERMES_API_SOCK is not defined! This will cause an issue unless the backend manually overwrites it")
}
helper := &BackendApplicationHelper{
Backend: backend,
SocketPath: socketPath,
}
return helper
}

View file

@ -0,0 +1,22 @@
package backendutil_custom
import (
"git.terah.dev/imterah/hermes/backend/commonbackend"
"git.terah.dev/imterah/hermes/backend/sshappbackend/datacommands"
)
type BackendInterface interface {
StartBackend(arguments []byte) (bool, error)
StopBackend() (bool, error)
GetBackendStatus() (bool, error)
StartProxy(command *commonbackend.AddProxy) (uint16, bool, error)
StopProxy(command *datacommands.RemoveProxy) (bool, error)
GetAllProxies() []uint16
ResolveProxy(proxyID uint16) *datacommands.ProxyInformationResponse
GetAllClientConnections(proxyID uint16) []uint16
ResolveConnection(connectionID uint16) *datacommands.ProxyConnectionsResponse
CheckParametersForConnections(clientParameters *commonbackend.CheckClientParameters) *commonbackend.CheckParametersResponse
CheckParametersForBackend(arguments []byte) *commonbackend.CheckParametersResponse
HandleTCPMessage(message *datacommands.TCPProxyData, data []byte)
HandleUDPMessage(message *datacommands.UDPProxyData, data []byte)
}

View file

@ -1,7 +1,120 @@
package main
import "fmt"
import (
"os"
"sync"
"git.terah.dev/imterah/hermes/backend/commonbackend"
"git.terah.dev/imterah/hermes/backend/sshappbackend/datacommands"
"git.terah.dev/imterah/hermes/backend/sshappbackend/remote-code/backendutil_custom"
"github.com/charmbracelet/log"
)
type TCPProxy struct {
proxyIDIndex uint16
proxyIDLock sync.Mutex
}
type UDPProxy struct {
}
type SSHRemoteAppBackend struct {
connectionIDIndex uint16
connectionIDLock sync.Mutex
tcpProxies map[uint16]*TCPProxy
udpProxies map[uint16]*UDPProxy
}
func (backend *SSHRemoteAppBackend) StartBackend(byte []byte) (bool, error) {
backend.tcpProxies = map[uint16]*TCPProxy{}
backend.udpProxies = map[uint16]*UDPProxy{}
return true, nil
}
func (backend *SSHRemoteAppBackend) StopBackend() (bool, error) {
return true, nil
}
func (backend *SSHRemoteAppBackend) GetBackendStatus() (bool, error) {
return true, nil
}
func (backend *SSHRemoteAppBackend) StartProxy(command *commonbackend.AddProxy) (uint16, bool, error) {
return 0, true, nil
}
func (backend *SSHRemoteAppBackend) StopProxy(command *datacommands.RemoveProxy) (bool, error) {
return true, nil
}
func (backend *SSHRemoteAppBackend) GetAllProxies() []uint16 {
return []uint16{}
}
func (backend *SSHRemoteAppBackend) ResolveProxy(proxyID uint16) *datacommands.ProxyInformationResponse {
return &datacommands.ProxyInformationResponse{}
}
func (backend *SSHRemoteAppBackend) GetAllClientConnections(proxyID uint16) []uint16 {
return []uint16{}
}
func (backend *SSHRemoteAppBackend) ResolveConnection(proxyID uint16) *datacommands.ProxyConnectionsResponse {
return &datacommands.ProxyConnectionsResponse{}
}
func (backend *SSHRemoteAppBackend) CheckParametersForConnections(clientParameters *commonbackend.CheckClientParameters) *commonbackend.CheckParametersResponse {
return &commonbackend.CheckParametersResponse{
IsValid: true,
Message: "Valid!",
}
}
func (backend *SSHRemoteAppBackend) CheckParametersForBackend(arguments []byte) *commonbackend.CheckParametersResponse {
return &commonbackend.CheckParametersResponse{
IsValid: true,
Message: "Valid!",
}
}
func (backend *SSHRemoteAppBackend) HandleTCPMessage(message *datacommands.TCPProxyData, data []byte) {
}
func (backend *SSHRemoteAppBackend) HandleUDPMessage(message *datacommands.UDPProxyData, data []byte) {
}
func main() {
fmt.Println("remottuh code")
logLevel := os.Getenv("HERMES_LOG_LEVEL")
if logLevel != "" {
switch logLevel {
case "debug":
log.SetLevel(log.DebugLevel)
case "info":
log.SetLevel(log.InfoLevel)
case "warn":
log.SetLevel(log.WarnLevel)
case "error":
log.SetLevel(log.ErrorLevel)
case "fatal":
log.SetLevel(log.FatalLevel)
}
}
backend := &SSHRemoteAppBackend{}
application := backendutil_custom.NewHelper(backend)
err := application.Start()
if err != nil {
log.Fatalf("failed execution in application: %s", err.Error())
}
}