From 3cb9526716632dd3f6a42dd7f26c809b86f19106 Mon Sep 17 00:00:00 2001 From: greysoh Date: Sun, 1 Dec 2024 22:07:10 -0500 Subject: [PATCH] feature: Adds more commands and adds an example. --- .gitignore | 7 +- api/gosrc/backendutil/application.go | 154 ++++++++++++++++++++ api/gosrc/backendutil/structure.go | 2 +- api/gosrc/commonbackend/constants.go | 78 +++++++++- api/gosrc/commonbackend/marshal.go | 8 +- api/gosrc/commonbackend/marshalling_test.go | 18 +-- api/gosrc/commonbackend/unmarshal.go | 12 +- api/gosrc/dummybackend/main.go | 83 +++++++++++ api/gosrc/externalbackendlauncher/main.go | 31 ++-- 9 files changed, 352 insertions(+), 41 deletions(-) create mode 100644 api/gosrc/backendutil/application.go create mode 100644 api/gosrc/dummybackend/main.go diff --git a/.gitignore b/.gitignore index 9c54740..71b4ac0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ +# Go artifacts +api/gosrc/sshbackend/sshbackend +api/gosrc/dummybackend/dummybackend +api/gosrc/externalbackendlauncher/externalbackendlauncher + # LOM lom/keys @@ -135,4 +140,4 @@ dist .yarn/install-state.gz .pnp.* -.tmp \ No newline at end of file +.tmp diff --git a/api/gosrc/backendutil/application.go b/api/gosrc/backendutil/application.go new file mode 100644 index 0000000..2308f01 --- /dev/null +++ b/api/gosrc/backendutil/application.go @@ -0,0 +1,154 @@ +package backendutil + +import ( + "fmt" + "net" + "os" + + "git.greysoh.dev/imterah/nextnet/commonbackend" + "github.com/charmbracelet/log" +) + +type BackendApplicationHelper struct { + Backend BackendInterface + SocketPath string + + socket net.Conn +} + +func (helper *BackendApplicationHelper) Start() error { + var err error + helper.socket, err = net.Dial("unix", helper.SocketPath) + + if err != nil { + return err + } + + for { + commandType, commandRaw, err := commonbackend.Unmarshal(helper.socket) + + if err != nil { + return err + } + + switch commandType { + case "start": + // TODO: implement response logic + command, ok := commandRaw.(*commonbackend.StartCommand) + + if !ok { + return fmt.Errorf("failed to typecast") + } + + // ok, err := + _, _ = helper.Backend.StartBackend(command.Arguments) + case "stop": + // TODO: implement response logic + _, ok := commandRaw.(*commonbackend.StopCommand) + + if !ok { + return fmt.Errorf("failed to typecast") + } + + _, _ = helper.Backend.StopBackend() + case "addConnection": + // TODO: implement response logic + command, ok := commandRaw.(*commonbackend.AddConnectionCommand) + + if !ok { + return fmt.Errorf("failed to typecast") + } + + _, _ = helper.Backend.AddConnection(command) + case "removeConnection": + // TODO: implement response logic + command, ok := commandRaw.(*commonbackend.RemoveConnectionCommand) + + if !ok { + return fmt.Errorf("failed to typecast") + } + + _, _ = helper.Backend.RemoveConnection(command) + case "getAllConnections": + _, ok := commandRaw.(*commonbackend.AddConnectionCommand) + + if !ok { + return fmt.Errorf("failed to typecast") + } + + connections := helper.Backend.GetAllConnections() + + serverParams := &commonbackend.ConnectionsResponse{ + Type: "connectionsResponse", + Connections: connections, + } + + byteData, err := commonbackend.Marshal(serverParams.Type, serverParams) + + if err != nil { + return err + } + + if _, err = helper.socket.Write(byteData); err != nil { + return err + } + case "checkClientParameters": + // TODO: implement response logic + 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": + // TODO: implement response logic + 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 + } + } + } +} + +func NewHelper(backend BackendInterface) *BackendApplicationHelper { + socketPath, ok := os.LookupEnv("NEXTNET_API_SOCK") + + if !ok { + log.Warn("NEXTNET_API_SOCK is not defined! This will cause an issue unless the backend manually overwrites it") + } + + helper := &BackendApplicationHelper{ + Backend: backend, + SocketPath: socketPath, + } + + return helper +} diff --git a/api/gosrc/backendutil/structure.go b/api/gosrc/backendutil/structure.go index 8a858da..acbfc37 100644 --- a/api/gosrc/backendutil/structure.go +++ b/api/gosrc/backendutil/structure.go @@ -3,7 +3,7 @@ package backendutil import "git.greysoh.dev/imterah/nextnet/commonbackend" type BackendInterface interface { - StartBackend() (bool, error) + StartBackend(arguments []byte) (bool, error) StopBackend() (bool, error) AddConnection(command *commonbackend.AddConnectionCommand) (bool, error) RemoveConnection(command *commonbackend.RemoveConnectionCommand) (bool, error) diff --git a/api/gosrc/commonbackend/constants.go b/api/gosrc/commonbackend/constants.go index 5b6f29d..0463a32 100644 --- a/api/gosrc/commonbackend/constants.go +++ b/api/gosrc/commonbackend/constants.go @@ -1,5 +1,17 @@ package commonbackend +// Not all of these structs are implemented commands. +// Currently unimplemented commands: +// GetAllConnectionsRequest +// BackendStatusResponse +// BackendStatusRequest +// ProxyStatusRequest +// ProxyStatusResponse +// GetAllConnectionsRequest + +// TODO (imterah): Rename AddConnectionCommand/RemoveConnectionCommand to AddProxyCommand/RemoveProxyCommand +// and their associated function calls + type StartCommand struct { Type string // Will be 'start' always Arguments []byte @@ -25,6 +37,51 @@ type RemoveConnectionCommand struct { Protocol string // Will be either 'tcp' or 'udp' } +type GetProxyStatus struct { + Type string // Will be 'getProxyStatus' always + SourceIP string + SourcePort uint16 + DestPort uint16 + Protocol string // Will be either 'tcp' or 'udp' +} + +type ProxyStatusResponse struct { + Type string // Will be 'proxyStatusResponse' always + SourceIP string + SourcePort uint16 + DestPort uint16 + Protocol string // Will be either 'tcp' or 'udp' + IsActive bool +} + +type ProxyConnection struct { + SourceIP string + SourcePort uint16 + DestPort uint16 + Protocol string // Will be either 'tcp' or 'udp' +} + +type ProxyConnectionResponse struct { + Type string // Will be 'proxyConnectionResponse' always + Connections []*ProxyConnection // List of connections +} + +type BackendStatusResponse struct { + Type string // Will be 'backendStatusResponse' always + InResponseTo string // Can be either for 'start' or 'stop' + StatusCode int // Either the 'Success' or 'Failure' constant + Message string // String message from the client (ex. failed to dial TCP) +} + +type BackendStatusRequest struct { + Type string // Will be 'backendStatusRequest' always + ForProperty string // Can be either for 'start' or 'stop' +} + +type GetAllConnectionsRequest struct { + Type string // Will be 'getAllConnectionsRequest' always +} + type ClientConnection struct { SourceIP string SourcePort uint16 @@ -33,8 +90,8 @@ type ClientConnection struct { ClientPort uint16 } -type GetAllConnections struct { - Type string // Will be 'getAllConnections' always +type ConnectionsResponse struct { + Type string // Will be 'connectionsResponse' always Connections []*ClientConnection // List of connections } @@ -53,10 +110,10 @@ type CheckServerParameters struct { // Sent as a response to either CheckClientParameters or CheckBackendParameters type CheckParametersResponse struct { - Type string // Will be 'checkParametersResponse' always - InReplyTo string // Will be either 'checkClientParameters' or 'checkServerParameters' - IsValid bool // If true, valid, and if false, invalid - Message string // String message from the client (ex. failed to unmarshal JSON: x is not defined) + Type string // Will be 'checkParametersResponse' always + InResponseTo string // Will be either 'checkClientParameters' or 'checkServerParameters' + IsValid bool // If true, valid, and if false, invalid + Message string // String message from the client (ex. failed to unmarshal JSON: x is not defined) } const ( @@ -74,10 +131,19 @@ const ( const ( TCP = iota UDP +) +const ( + StatusSuccess = iota + StatusFailure +) + +const ( + // IP versions IPv4 = 4 IPv6 = 6 + // TODO: net has these constants defined already. We should switch to these IPv4Size = 4 IPv6Size = 16 ) diff --git a/api/gosrc/commonbackend/marshal.go b/api/gosrc/commonbackend/marshal.go index fa215e3..405f60d 100644 --- a/api/gosrc/commonbackend/marshal.go +++ b/api/gosrc/commonbackend/marshal.go @@ -156,8 +156,8 @@ func Marshal(commandType string, command interface{}) ([]byte, error) { removeConnectionBytes[6+len(ipBytes)] = protocol return removeConnectionBytes, nil - case "getAllConnections": - allConnectionsCommand, ok := command.(*GetAllConnections) + case "connectionsResponse": + allConnectionsCommand, ok := command.(*ConnectionsResponse) if !ok { return nil, fmt.Errorf("failed to typecast") @@ -247,9 +247,9 @@ func Marshal(commandType string, command interface{}) ([]byte, error) { var checkMethod uint8 - if checkParametersCommand.InReplyTo == "checkClientParameters" { + if checkParametersCommand.InResponseTo == "checkClientParameters" { checkMethod = CheckClientParametersID - } else if checkParametersCommand.InReplyTo == "checkServerParameters" { + } else if checkParametersCommand.InResponseTo == "checkServerParameters" { checkMethod = CheckServerParametersID } else { return nil, fmt.Errorf("invalid mode recieved (must be either checkClientParameters or checkServerParameters)") diff --git a/api/gosrc/commonbackend/marshalling_test.go b/api/gosrc/commonbackend/marshalling_test.go index edf8c8f..f339809 100644 --- a/api/gosrc/commonbackend/marshalling_test.go +++ b/api/gosrc/commonbackend/marshalling_test.go @@ -219,8 +219,8 @@ func TestRemoveConnectionCommandMarshalSupport(t *testing.T) { } func TestGetAllConnectionsCommandMarshalSupport(t *testing.T) { - commandInput := &GetAllConnections{ - Type: "getAllConnections", + commandInput := &ConnectionsResponse{ + Type: "connectionsResponse", Connections: []*ClientConnection{ { SourceIP: "127.0.0.1", @@ -268,7 +268,7 @@ func TestGetAllConnectionsCommandMarshalSupport(t *testing.T) { log.Print("command type does not match up!") } - commandUnmarshalled, ok := commandUnmarshalledRaw.(*GetAllConnections) + commandUnmarshalled, ok := commandUnmarshalledRaw.(*ConnectionsResponse) if !ok { t.Fatal("failed typecast") @@ -418,10 +418,10 @@ func TestCheckServerParametersMarshalSupport(t *testing.T) { func TestCheckParametersResponseMarshalSupport(t *testing.T) { commandInput := &CheckParametersResponse{ - Type: "checkParametersResponse", - InReplyTo: "checkClientParameters", - IsValid: true, - Message: "Hello from automated testing", + Type: "checkParametersResponse", + InResponseTo: "checkClientParameters", + IsValid: true, + Message: "Hello from automated testing", } commandMarshalled, err := Marshal(commandInput.Type, commandInput) @@ -457,9 +457,9 @@ func TestCheckParametersResponseMarshalSupport(t *testing.T) { log.Printf("Types are not equal (orig: %s, unmsh: %s)", commandInput.Type, commandUnmarshalled.Type) } - if commandInput.InReplyTo != commandUnmarshalled.InReplyTo { + if commandInput.InResponseTo != commandUnmarshalled.InResponseTo { t.Fail() - log.Printf("InReplyTo's are not equal (orig: %s, unmsh: %s)", commandInput.InReplyTo, commandUnmarshalled.InReplyTo) + log.Printf("InResponseTo's are not equal (orig: %s, unmsh: %s)", commandInput.InResponseTo, commandUnmarshalled.InResponseTo) } if commandInput.IsValid != commandUnmarshalled.IsValid { diff --git a/api/gosrc/commonbackend/unmarshal.go b/api/gosrc/commonbackend/unmarshal.go index c14c762..bab6949 100644 --- a/api/gosrc/commonbackend/unmarshal.go +++ b/api/gosrc/commonbackend/unmarshal.go @@ -254,8 +254,8 @@ func Unmarshal(conn io.Reader) (string, interface{}, error) { } } - return "getAllConnections", &GetAllConnections{ - Type: "getAllConnections", + return "connectionsResponse", &ConnectionsResponse{ + Type: "connectionsResponse", Connections: connections, }, errorReturn case CheckClientParametersID: @@ -376,10 +376,10 @@ func Unmarshal(conn io.Reader) (string, interface{}, error) { } return "checkParametersResponse", &CheckParametersResponse{ - Type: "checkParametersResponse", - InReplyTo: checkMethod, - IsValid: isValid[0] == 1, - Message: message, + Type: "checkParametersResponse", + InResponseTo: checkMethod, + IsValid: isValid[0] == 1, + Message: message, }, nil } diff --git a/api/gosrc/dummybackend/main.go b/api/gosrc/dummybackend/main.go new file mode 100644 index 0000000..1adf96c --- /dev/null +++ b/api/gosrc/dummybackend/main.go @@ -0,0 +1,83 @@ +package main + +import ( + "os" + + "git.greysoh.dev/imterah/nextnet/backendutil" + "git.greysoh.dev/imterah/nextnet/commonbackend" + "github.com/charmbracelet/log" +) + +type DummyBackend struct { +} + +func (backend *DummyBackend) StartBackend(byte []byte) (bool, error) { + return true, nil +} + +func (backend *DummyBackend) StopBackend() (bool, error) { + return true, nil +} + +func (backend *DummyBackend) AddConnection(command *commonbackend.AddConnectionCommand) (bool, error) { + return true, nil +} + +func (backend *DummyBackend) RemoveConnection(command *commonbackend.RemoveConnectionCommand) (bool, error) { + return true, nil +} + +func (backend *DummyBackend) GetAllConnections() []*commonbackend.ClientConnection { + return []*commonbackend.ClientConnection{} +} + +func (backend *DummyBackend) CheckParametersForConnections(clientParameters *commonbackend.CheckClientParameters) *commonbackend.CheckParametersResponse { + // You don't have to specify Type and InReplyTo. Those will be handled for you. + // Message is optional. + return &commonbackend.CheckParametersResponse{ + IsValid: true, + Message: "Valid!", + } +} + +func (backend *DummyBackend) CheckParametersForBackend(arguments []byte) *commonbackend.CheckParametersResponse { + // You don't have to specify Type and InReplyTo. Those will be handled for you. + // Message is optional. + return &commonbackend.CheckParametersResponse{ + IsValid: true, + Message: "Valid!", + } +} + +func main() { + // When using logging, you should use charmbracelet/log, because that's what everything else uses in this ecosystem of a project. - imterah + logLevel := os.Getenv("NEXTNET_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 := &DummyBackend{} + + application := backendutil.NewHelper(backend) + err := application.Start() + + if err != nil { + log.Fatalf("failed execution in application: %s", err.Error()) + } +} diff --git a/api/gosrc/externalbackendlauncher/main.go b/api/gosrc/externalbackendlauncher/main.go index b5d1544..9fe4983 100644 --- a/api/gosrc/externalbackendlauncher/main.go +++ b/api/gosrc/externalbackendlauncher/main.go @@ -33,23 +33,25 @@ func main() { tempDir, err := os.MkdirTemp("", "nextnet-sockets-") logLevel := os.Getenv("NEXTNET_LOG_LEVEL") - if logLevel != "" { - switch logLevel { - case "debug": - log.SetLevel(log.DebugLevel) + if logLevel == "" { + logLevel = "fatal" + } - case "info": - log.SetLevel(log.InfoLevel) + switch logLevel { + case "debug": + log.SetLevel(log.DebugLevel) - case "warn": - log.SetLevel(log.WarnLevel) + case "info": + log.SetLevel(log.InfoLevel) - case "error": - log.SetLevel(log.ErrorLevel) + case "warn": + log.SetLevel(log.WarnLevel) - case "fatal": - log.SetLevel(log.FatalLevel) - } + case "error": + log.SetLevel(log.ErrorLevel) + + case "fatal": + log.SetLevel(log.FatalLevel) } if len(os.Args) != 3 { @@ -109,8 +111,9 @@ func main() { for { log.Info("starting process...") // TODO: can we reuse cmd? + cmd := exec.Command(executablePath) - cmd.Env = append(cmd.Env, fmt.Sprintf("NEXTNET_API_SOCK=%s", sockPath)) + cmd.Env = append(cmd.Env, fmt.Sprintf("NEXTNET_API_SOCK=%s", sockPath), fmt.Sprintf("NEXTNET_LOG_LEVEL=%s", logLevel)) cmd.Stdout = stdout cmd.Stderr = stderr