diff --git a/backend/commonbackend/constants.go b/backend/commonbackend/constants.go new file mode 100644 index 0000000..cdb68f2 --- /dev/null +++ b/backend/commonbackend/constants.go @@ -0,0 +1,147 @@ +package commonbackend + +type Start struct { + Type string // Will be 'start' always + Arguments []byte +} + +type Stop struct { + Type string // Will be 'stop' always +} + +type AddProxy struct { + Type string // Will be 'addProxy' always + SourceIP string + SourcePort uint16 + DestPort uint16 + Protocol string // Will be either 'tcp' or 'udp' +} + +type RemoveProxy struct { + Type string // Will be 'removeProxy' always + SourceIP string + SourcePort uint16 + DestPort uint16 + Protocol string // Will be either 'tcp' or 'udp' +} + +type ProxyStatusRequest struct { + Type string // Will be 'proxyStatusRequest' 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 ProxyInstance struct { + SourceIP string + SourcePort uint16 + DestPort uint16 + Protocol string // Will be either 'tcp' or 'udp' +} + +type ProxyInstanceResponse struct { + Type string // Will be 'proxyConnectionResponse' always + Proxies []*ProxyInstance // List of connections +} + +type ProxyInstanceRequest struct { + Type string // Will be 'proxyConnectionRequest' always +} + +type BackendStatusResponse struct { + Type string // Will be 'backendStatusResponse' always + IsRunning bool // True if running, false if not running + 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 +} + +type ProxyConnectionsRequest struct { + Type string // Will be 'proxyConnectionsRequest' always +} + +// Client's connection to a specific proxy +type ProxyClientConnection struct { + SourceIP string + SourcePort uint16 + DestPort uint16 + ClientIP string + ClientPort uint16 +} + +type ProxyConnectionsResponse struct { + Type string // Will be 'proxyConnectionsResponse' always + Connections []*ProxyClientConnection // List of connections +} + +type CheckClientParameters struct { + Type string // Will be 'checkClientParameters' always + SourceIP string + SourcePort uint16 + DestPort uint16 + Protocol string // Will be either 'tcp' or 'udp' +} + +type CheckServerParameters struct { + Type string // Will be 'checkServerParameters' always + Arguments []byte +} + +// Sent as a response to either CheckClientParameters or CheckBackendParameters +type CheckParametersResponse struct { + 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 ( + StartID = iota + StopID + AddProxyID + RemoveProxyID + ProxyConnectionsResponseID + CheckClientParametersID + CheckServerParametersID + CheckParametersResponseID + ProxyConnectionsRequestID + BackendStatusResponseID + BackendStatusRequestID + ProxyStatusRequestID + ProxyStatusResponseID + ProxyInstanceResponseID + ProxyInstanceRequestID +) + +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/backend/commonbackend/marshal.go b/backend/commonbackend/marshal.go new file mode 100644 index 0000000..6baf02e --- /dev/null +++ b/backend/commonbackend/marshal.go @@ -0,0 +1,507 @@ +package commonbackend + +import ( + "encoding/binary" + "fmt" + "net" +) + +func marshalIndividualConnectionStruct(conn *ProxyClientConnection) []byte { + sourceIPOriginal := net.ParseIP(conn.SourceIP) + clientIPOriginal := net.ParseIP(conn.ClientIP) + + var serverIPVer uint8 + var sourceIP []byte + + if sourceIPOriginal.To4() == nil { + serverIPVer = IPv6 + sourceIP = sourceIPOriginal.To16() + } else { + serverIPVer = IPv4 + sourceIP = sourceIPOriginal.To4() + } + + var clientIPVer uint8 + var clientIP []byte + + if clientIPOriginal.To4() == nil { + clientIPVer = IPv6 + clientIP = clientIPOriginal.To16() + } else { + clientIPVer = IPv4 + clientIP = clientIPOriginal.To4() + } + + connectionBlock := make([]byte, 8+len(sourceIP)+len(clientIP)) + + connectionBlock[0] = serverIPVer + copy(connectionBlock[1:len(sourceIP)+1], sourceIP) + + binary.BigEndian.PutUint16(connectionBlock[1+len(sourceIP):3+len(sourceIP)], conn.SourcePort) + binary.BigEndian.PutUint16(connectionBlock[3+len(sourceIP):5+len(sourceIP)], conn.DestPort) + + connectionBlock[5+len(sourceIP)] = clientIPVer + copy(connectionBlock[6+len(sourceIP):6+len(sourceIP)+len(clientIP)], clientIP) + binary.BigEndian.PutUint16(connectionBlock[6+len(sourceIP)+len(clientIP):8+len(sourceIP)+len(clientIP)], conn.ClientPort) + + return connectionBlock +} + +func marshalIndividualProxyStruct(conn *ProxyInstance) ([]byte, error) { + sourceIPOriginal := net.ParseIP(conn.SourceIP) + + var sourceIPVer uint8 + var sourceIP []byte + + if sourceIPOriginal.To4() == nil { + sourceIPVer = IPv6 + sourceIP = sourceIPOriginal.To16() + } else { + sourceIPVer = IPv4 + sourceIP = sourceIPOriginal.To4() + } + + proxyBlock := make([]byte, 6+len(sourceIP)) + + proxyBlock[0] = sourceIPVer + copy(proxyBlock[1:len(sourceIP)+1], sourceIP) + + binary.BigEndian.PutUint16(proxyBlock[1+len(sourceIP):3+len(sourceIP)], conn.SourcePort) + binary.BigEndian.PutUint16(proxyBlock[3+len(sourceIP):5+len(sourceIP)], conn.DestPort) + + var protocolVersion uint8 + + if conn.Protocol == "tcp" { + protocolVersion = TCP + } else if conn.Protocol == "udp" { + protocolVersion = UDP + } else { + return proxyBlock, fmt.Errorf("invalid protocol recieved") + } + + proxyBlock[5+len(sourceIP)] = protocolVersion + + return proxyBlock, nil +} + +func Marshal(commandType string, command interface{}) ([]byte, error) { + switch commandType { + case "start": + startCommand, ok := command.(*Start) + + if !ok { + return nil, fmt.Errorf("failed to typecast") + } + + startCommandBytes := make([]byte, 1+2+len(startCommand.Arguments)) + startCommandBytes[0] = StartID + binary.BigEndian.PutUint16(startCommandBytes[1:3], uint16(len(startCommand.Arguments))) + copy(startCommandBytes[3:], startCommand.Arguments) + + return startCommandBytes, nil + case "stop": + _, ok := command.(*Stop) + + if !ok { + return nil, fmt.Errorf("failed to typecast") + } + + return []byte{StopID}, nil + case "addProxy": + addConnectionCommand, ok := command.(*AddProxy) + + if !ok { + return nil, fmt.Errorf("failed to typecast") + } + + sourceIP := net.ParseIP(addConnectionCommand.SourceIP) + + var ipVer uint8 + var ipBytes []byte + + if sourceIP.To4() == nil { + ipBytes = sourceIP.To16() + ipVer = IPv6 + } else { + ipBytes = sourceIP.To4() + ipVer = IPv4 + } + + addConnectionBytes := make([]byte, 1+1+len(ipBytes)+2+2+1) + + addConnectionBytes[0] = AddProxyID + addConnectionBytes[1] = ipVer + + copy(addConnectionBytes[2:2+len(ipBytes)], ipBytes) + + binary.BigEndian.PutUint16(addConnectionBytes[2+len(ipBytes):4+len(ipBytes)], addConnectionCommand.SourcePort) + binary.BigEndian.PutUint16(addConnectionBytes[4+len(ipBytes):6+len(ipBytes)], addConnectionCommand.DestPort) + + var protocol uint8 + + if addConnectionCommand.Protocol == "tcp" { + protocol = TCP + } else if addConnectionCommand.Protocol == "udp" { + protocol = UDP + } else { + return nil, fmt.Errorf("invalid protocol") + } + + addConnectionBytes[6+len(ipBytes)] = protocol + + return addConnectionBytes, nil + case "removeProxy": + removeConnectionCommand, ok := command.(*RemoveProxy) + + if !ok { + return nil, fmt.Errorf("failed to typecast") + } + + sourceIP := net.ParseIP(removeConnectionCommand.SourceIP) + + var ipVer uint8 + var ipBytes []byte + + if sourceIP.To4() == nil { + ipBytes = sourceIP.To16() + ipVer = IPv6 + } else { + ipBytes = sourceIP.To4() + ipVer = IPv4 + } + + removeConnectionBytes := make([]byte, 1+1+len(ipBytes)+2+2+1) + + removeConnectionBytes[0] = RemoveProxyID + removeConnectionBytes[1] = ipVer + copy(removeConnectionBytes[2:2+len(ipBytes)], ipBytes) + binary.BigEndian.PutUint16(removeConnectionBytes[2+len(ipBytes):4+len(ipBytes)], removeConnectionCommand.SourcePort) + binary.BigEndian.PutUint16(removeConnectionBytes[4+len(ipBytes):6+len(ipBytes)], removeConnectionCommand.DestPort) + + var protocol uint8 + + if removeConnectionCommand.Protocol == "tcp" { + protocol = TCP + } else if removeConnectionCommand.Protocol == "udp" { + protocol = UDP + } else { + return nil, fmt.Errorf("invalid protocol") + } + + removeConnectionBytes[6+len(ipBytes)] = protocol + + return removeConnectionBytes, nil + case "proxyConnectionsResponse": + allConnectionsCommand, ok := command.(*ProxyConnectionsResponse) + + if !ok { + return nil, fmt.Errorf("failed to typecast") + } + + connectionsArray := make([][]byte, len(allConnectionsCommand.Connections)) + totalSize := 0 + + for connIndex, conn := range allConnectionsCommand.Connections { + connectionsArray[connIndex] = marshalIndividualConnectionStruct(conn) + totalSize += len(connectionsArray[connIndex]) + 1 + } + + if totalSize == 0 { + totalSize = 1 + } + + connectionCommandArray := make([]byte, totalSize+1) + connectionCommandArray[0] = ProxyConnectionsResponseID + + currentPosition := 1 + + for _, connection := range connectionsArray { + copy(connectionCommandArray[currentPosition:currentPosition+len(connection)], connection) + connectionCommandArray[currentPosition+len(connection)] = '\r' + currentPosition += len(connection) + 1 + } + + connectionCommandArray[totalSize] = '\n' + return connectionCommandArray, nil + case "checkClientParameters": + checkClientCommand, ok := command.(*CheckClientParameters) + + if !ok { + return nil, fmt.Errorf("failed to typecast") + } + + sourceIP := net.ParseIP(checkClientCommand.SourceIP) + + var ipVer uint8 + var ipBytes []byte + + if sourceIP.To4() == nil { + ipBytes = sourceIP.To16() + ipVer = IPv6 + } else { + ipBytes = sourceIP.To4() + ipVer = IPv4 + } + + checkClientBytes := make([]byte, 1+1+len(ipBytes)+2+2+1) + + checkClientBytes[0] = CheckClientParametersID + checkClientBytes[1] = ipVer + copy(checkClientBytes[2:2+len(ipBytes)], ipBytes) + binary.BigEndian.PutUint16(checkClientBytes[2+len(ipBytes):4+len(ipBytes)], checkClientCommand.SourcePort) + binary.BigEndian.PutUint16(checkClientBytes[4+len(ipBytes):6+len(ipBytes)], checkClientCommand.DestPort) + + var protocol uint8 + + if checkClientCommand.Protocol == "tcp" { + protocol = TCP + } else if checkClientCommand.Protocol == "udp" { + protocol = UDP + } else { + return nil, fmt.Errorf("invalid protocol") + } + + checkClientBytes[6+len(ipBytes)] = protocol + + return checkClientBytes, nil + case "checkServerParameters": + checkServerCommand, ok := command.(*CheckServerParameters) + + if !ok { + return nil, fmt.Errorf("failed to typecast") + } + + serverCommandBytes := make([]byte, 1+2+len(checkServerCommand.Arguments)) + serverCommandBytes[0] = CheckServerParametersID + binary.BigEndian.PutUint16(serverCommandBytes[1:3], uint16(len(checkServerCommand.Arguments))) + copy(serverCommandBytes[3:], checkServerCommand.Arguments) + + return serverCommandBytes, nil + case "checkParametersResponse": + checkParametersCommand, ok := command.(*CheckParametersResponse) + + if !ok { + return nil, fmt.Errorf("failed to typecast") + } + + var checkMethod uint8 + + if checkParametersCommand.InResponseTo == "checkClientParameters" { + checkMethod = CheckClientParametersID + } else if checkParametersCommand.InResponseTo == "checkServerParameters" { + checkMethod = CheckServerParametersID + } else { + return nil, fmt.Errorf("invalid mode recieved (must be either checkClientParameters or checkServerParameters)") + } + + var isValid uint8 + + if checkParametersCommand.IsValid { + isValid = 1 + } + + checkResponseBytes := make([]byte, 3+2+len(checkParametersCommand.Message)) + checkResponseBytes[0] = CheckParametersResponseID + checkResponseBytes[1] = checkMethod + checkResponseBytes[2] = isValid + + binary.BigEndian.PutUint16(checkResponseBytes[3:5], uint16(len(checkParametersCommand.Message))) + + if len(checkParametersCommand.Message) != 0 { + copy(checkResponseBytes[5:], []byte(checkParametersCommand.Message)) + } + + return checkResponseBytes, nil + case "backendStatusResponse": + backendStatusResponse, ok := command.(*BackendStatusResponse) + + if !ok { + return nil, fmt.Errorf("failed to typecast") + } + + var isRunning uint8 + + if backendStatusResponse.IsRunning { + isRunning = 1 + } else { + isRunning = 0 + } + + statusResponseBytes := make([]byte, 3+2+len(backendStatusResponse.Message)) + statusResponseBytes[0] = BackendStatusResponseID + statusResponseBytes[1] = isRunning + statusResponseBytes[2] = byte(backendStatusResponse.StatusCode) + + binary.BigEndian.PutUint16(statusResponseBytes[3:5], uint16(len(backendStatusResponse.Message))) + + if len(backendStatusResponse.Message) != 0 { + copy(statusResponseBytes[5:], []byte(backendStatusResponse.Message)) + } + + return statusResponseBytes, nil + case "backendStatusRequest": + _, ok := command.(*BackendStatusRequest) + + if !ok { + return nil, fmt.Errorf("failed to typecast") + } + + statusRequestBytes := make([]byte, 1) + statusRequestBytes[0] = BackendStatusRequestID + + return statusRequestBytes, nil + case "proxyStatusRequest": + proxyStatusRequest, ok := command.(*ProxyStatusRequest) + + if !ok { + return nil, fmt.Errorf("failed to typecast") + } + + sourceIP := net.ParseIP(proxyStatusRequest.SourceIP) + + var ipVer uint8 + var ipBytes []byte + + if sourceIP.To4() == nil { + ipBytes = sourceIP.To16() + ipVer = IPv6 + } else { + ipBytes = sourceIP.To4() + ipVer = IPv4 + } + + proxyStatusRequestBytes := make([]byte, 1+1+len(ipBytes)+2+2+1) + + proxyStatusRequestBytes[0] = ProxyStatusRequestID + proxyStatusRequestBytes[1] = ipVer + + copy(proxyStatusRequestBytes[2:2+len(ipBytes)], ipBytes) + + binary.BigEndian.PutUint16(proxyStatusRequestBytes[2+len(ipBytes):4+len(ipBytes)], proxyStatusRequest.SourcePort) + binary.BigEndian.PutUint16(proxyStatusRequestBytes[4+len(ipBytes):6+len(ipBytes)], proxyStatusRequest.DestPort) + + var protocol uint8 + + if proxyStatusRequest.Protocol == "tcp" { + protocol = TCP + } else if proxyStatusRequest.Protocol == "udp" { + protocol = UDP + } else { + return nil, fmt.Errorf("invalid protocol") + } + + proxyStatusRequestBytes[6+len(ipBytes)] = protocol + + return proxyStatusRequestBytes, nil + case "proxyStatusResponse": + proxyStatusResponse, ok := command.(*ProxyStatusResponse) + + if !ok { + return nil, fmt.Errorf("failed to typecast") + } + + sourceIP := net.ParseIP(proxyStatusResponse.SourceIP) + + var ipVer uint8 + var ipBytes []byte + + if sourceIP.To4() == nil { + ipBytes = sourceIP.To16() + ipVer = IPv6 + } else { + ipBytes = sourceIP.To4() + ipVer = IPv4 + } + + proxyStatusResponseBytes := make([]byte, 1+1+len(ipBytes)+2+2+1+1) + + proxyStatusResponseBytes[0] = ProxyStatusResponseID + proxyStatusResponseBytes[1] = ipVer + + copy(proxyStatusResponseBytes[2:2+len(ipBytes)], ipBytes) + + binary.BigEndian.PutUint16(proxyStatusResponseBytes[2+len(ipBytes):4+len(ipBytes)], proxyStatusResponse.SourcePort) + binary.BigEndian.PutUint16(proxyStatusResponseBytes[4+len(ipBytes):6+len(ipBytes)], proxyStatusResponse.DestPort) + + var protocol uint8 + + if proxyStatusResponse.Protocol == "tcp" { + protocol = TCP + } else if proxyStatusResponse.Protocol == "udp" { + protocol = UDP + } else { + return nil, fmt.Errorf("invalid protocol") + } + + proxyStatusResponseBytes[6+len(ipBytes)] = protocol + + var isActive uint8 + + if proxyStatusResponse.IsActive { + isActive = 1 + } else { + isActive = 0 + } + + proxyStatusResponseBytes[7+len(ipBytes)] = isActive + + return proxyStatusResponseBytes, nil + case "proxyInstanceResponse": + proxyConectionResponse, ok := command.(*ProxyInstanceResponse) + + if !ok { + return nil, fmt.Errorf("failed to typecast") + } + + proxyArray := make([][]byte, len(proxyConectionResponse.Proxies)) + totalSize := 0 + + for proxyIndex, proxy := range proxyConectionResponse.Proxies { + var err error + proxyArray[proxyIndex], err = marshalIndividualProxyStruct(proxy) + + if err != nil { + return nil, err + } + + totalSize += len(proxyArray[proxyIndex]) + 1 + } + + if totalSize == 0 { + totalSize = 1 + } + + connectionCommandArray := make([]byte, totalSize+1) + connectionCommandArray[0] = ProxyInstanceResponseID + + currentPosition := 1 + + for _, connection := range proxyArray { + copy(connectionCommandArray[currentPosition:currentPosition+len(connection)], connection) + connectionCommandArray[currentPosition+len(connection)] = '\r' + currentPosition += len(connection) + 1 + } + + connectionCommandArray[totalSize] = '\n' + + return connectionCommandArray, nil + case "proxyInstanceRequest": + _, ok := command.(*ProxyInstanceRequest) + + if !ok { + return nil, fmt.Errorf("failed to typecast") + } + + return []byte{ProxyInstanceRequestID}, nil + case "proxyConnectionsRequest": + _, ok := command.(*ProxyConnectionsRequest) + + if !ok { + return nil, fmt.Errorf("failed to typecast") + } + + return []byte{ProxyConnectionsRequestID}, nil + } + + return nil, fmt.Errorf("couldn't match command name") +} diff --git a/backend/commonbackend/marshalling_test.go b/backend/commonbackend/marshalling_test.go new file mode 100644 index 0000000..1c93f94 --- /dev/null +++ b/backend/commonbackend/marshalling_test.go @@ -0,0 +1,824 @@ +package commonbackend + +import ( + "bytes" + "log" + "os" + "testing" +) + +var logLevel = os.Getenv("HERMES_LOG_LEVEL") + +func TestStartCommandMarshalSupport(t *testing.T) { + commandInput := &Start{ + Type: "start", + Arguments: []byte("Hello from automated testing"), + } + + commandMarshalled, err := Marshal(commandInput.Type, commandInput) + + if logLevel == "debug" { + log.Printf("Generated array contents: %v", commandMarshalled) + } + + if err != nil { + t.Fatal(err.Error()) + } + + buf := bytes.NewBuffer(commandMarshalled) + commandType, commandUnmarshalledRaw, err := Unmarshal(buf) + + if err != nil { + t.Fatal(err.Error()) + } + + if commandType != commandInput.Type { + t.Fail() + log.Print("command type does not match up!") + } + + commandUnmarshalled, ok := commandUnmarshalledRaw.(*Start) + + if !ok { + t.Fatal("failed typecast") + } + + if commandInput.Type != commandUnmarshalled.Type { + t.Fail() + log.Printf("Types are not equal (orig: %s, unmsh: %s)", commandInput.Type, commandUnmarshalled.Type) + } + + if !bytes.Equal(commandInput.Arguments, commandUnmarshalled.Arguments) { + log.Fatalf("Arguments are not equal (orig: '%s', unmsh: '%s')", string(commandInput.Arguments), string(commandUnmarshalled.Arguments)) + } +} + +func TestStopCommandMarshalSupport(t *testing.T) { + commandInput := &Stop{ + Type: "stop", + } + + commandMarshalled, err := Marshal(commandInput.Type, commandInput) + + if logLevel == "debug" { + log.Printf("Generated array contents: %v", commandMarshalled) + } + + if err != nil { + t.Fatal(err.Error()) + } + + buf := bytes.NewBuffer(commandMarshalled) + commandType, commandUnmarshalledRaw, err := Unmarshal(buf) + + if err != nil { + t.Fatal(err.Error()) + } + + if commandType != commandInput.Type { + t.Fail() + log.Print("command type does not match up!") + } + + commandUnmarshalled, ok := commandUnmarshalledRaw.(*Stop) + + if !ok { + t.Fatal("failed typecast") + } + + if commandInput.Type != commandUnmarshalled.Type { + t.Fail() + log.Printf("Types are not equal (orig: %s, unmsh: %s)", commandInput.Type, commandUnmarshalled.Type) + } +} + +func TestAddConnectionCommandMarshalSupport(t *testing.T) { + commandInput := &AddProxy{ + Type: "addProxy", + SourceIP: "192.168.0.139", + SourcePort: 19132, + DestPort: 19132, + Protocol: "tcp", + } + + commandMarshalled, err := Marshal(commandInput.Type, commandInput) + + if logLevel == "debug" { + log.Printf("Generated array contents: %v", commandMarshalled) + } + + if err != nil { + t.Fatal(err.Error()) + } + + buf := bytes.NewBuffer(commandMarshalled) + commandType, commandUnmarshalledRaw, err := Unmarshal(buf) + + if err != nil { + t.Fatal(err.Error()) + } + + if commandType != commandInput.Type { + t.Fail() + log.Print("command type does not match up!") + } + + commandUnmarshalled, ok := commandUnmarshalledRaw.(*AddProxy) + + if !ok { + t.Fatal("failed typecast") + } + + if commandInput.Type != commandUnmarshalled.Type { + t.Fail() + log.Printf("Types are not equal (orig: %s, unmsh: %s)", commandInput.Type, commandUnmarshalled.Type) + } + + if commandInput.SourceIP != commandUnmarshalled.SourceIP { + t.Fail() + log.Printf("SourceIP's are not equal (orig: %s, unmsh: %s)", commandInput.SourceIP, commandUnmarshalled.SourceIP) + } + + if commandInput.SourcePort != commandUnmarshalled.SourcePort { + t.Fail() + log.Printf("SourcePort's are not equal (orig: %d, unmsh: %d)", commandInput.SourcePort, commandUnmarshalled.SourcePort) + } + + if commandInput.DestPort != commandUnmarshalled.DestPort { + t.Fail() + log.Printf("DestPort's are not equal (orig: %d, unmsh: %d)", commandInput.DestPort, commandUnmarshalled.DestPort) + } + + if commandInput.Protocol != commandUnmarshalled.Protocol { + t.Fail() + log.Printf("Protocols are not equal (orig: %s, unmsh: %s)", commandInput.Protocol, commandUnmarshalled.Protocol) + } +} + +func TestRemoveConnectionCommandMarshalSupport(t *testing.T) { + commandInput := &RemoveProxy{ + Type: "removeProxy", + SourceIP: "192.168.0.139", + SourcePort: 19132, + DestPort: 19132, + Protocol: "tcp", + } + + commandMarshalled, err := Marshal(commandInput.Type, commandInput) + + if err != nil { + t.Fatal(err.Error()) + } + + if logLevel == "debug" { + log.Printf("Generated array contents: %v", commandMarshalled) + } + + buf := bytes.NewBuffer(commandMarshalled) + commandType, commandUnmarshalledRaw, err := Unmarshal(buf) + + if err != nil { + t.Fatal(err.Error()) + } + + if commandType != commandInput.Type { + t.Fail() + log.Print("command type does not match up!") + } + + commandUnmarshalled, ok := commandUnmarshalledRaw.(*RemoveProxy) + + if !ok { + t.Fatal("failed typecast") + } + + if commandInput.Type != commandUnmarshalled.Type { + t.Fail() + log.Printf("Types are not equal (orig: %s, unmsh: %s)", commandInput.Type, commandUnmarshalled.Type) + } + + if commandInput.SourceIP != commandUnmarshalled.SourceIP { + t.Fail() + log.Printf("SourceIP's are not equal (orig: %s, unmsh: %s)", commandInput.SourceIP, commandUnmarshalled.SourceIP) + } + + if commandInput.SourcePort != commandUnmarshalled.SourcePort { + t.Fail() + log.Printf("SourcePort's are not equal (orig: %d, unmsh: %d)", commandInput.SourcePort, commandUnmarshalled.SourcePort) + } + + if commandInput.DestPort != commandUnmarshalled.DestPort { + t.Fail() + log.Printf("DestPort's are not equal (orig: %d, unmsh: %d)", commandInput.DestPort, commandUnmarshalled.DestPort) + } + + if commandInput.Protocol != commandUnmarshalled.Protocol { + t.Fail() + log.Printf("Protocols are not equal (orig: %s, unmsh: %s)", commandInput.Protocol, commandUnmarshalled.Protocol) + } +} + +func TestGetAllConnectionsCommandMarshalSupport(t *testing.T) { + commandInput := &ProxyConnectionsResponse{ + Type: "proxyConnectionsResponse", + Connections: []*ProxyClientConnection{ + { + SourceIP: "127.0.0.1", + SourcePort: 19132, + DestPort: 19132, + ClientIP: "127.0.0.1", + ClientPort: 12321, + }, + { + SourceIP: "127.0.0.1", + SourcePort: 19132, + DestPort: 19132, + ClientIP: "192.168.0.168", + ClientPort: 23457, + }, + { + SourceIP: "127.0.0.1", + SourcePort: 19132, + DestPort: 19132, + ClientIP: "68.42.203.47", + ClientPort: 38721, + }, + }, + } + + commandMarshalled, err := Marshal(commandInput.Type, commandInput) + + if err != nil { + t.Fatal(err.Error()) + } + + if logLevel == "debug" { + log.Printf("Generated array contents: %v", commandMarshalled) + } + + buf := bytes.NewBuffer(commandMarshalled) + commandType, commandUnmarshalledRaw, err := Unmarshal(buf) + + if err != nil { + t.Fatal(err.Error()) + } + + if commandType != commandInput.Type { + t.Fail() + log.Print("command type does not match up!") + } + + commandUnmarshalled, ok := commandUnmarshalledRaw.(*ProxyConnectionsResponse) + + if !ok { + t.Fatal("failed typecast") + } + + if commandInput.Type != commandUnmarshalled.Type { + t.Fail() + log.Printf("Types are not equal (orig: %s, unmsh: %s)", commandInput.Type, commandUnmarshalled.Type) + } + + for commandIndex, originalConnection := range commandInput.Connections { + remoteConnection := commandUnmarshalled.Connections[commandIndex] + + if originalConnection.SourceIP != remoteConnection.SourceIP { + t.Fail() + log.Printf("(in #%d) SourceIP's are not equal (orig: %s, unmsh: %s)", commandIndex, originalConnection.SourceIP, remoteConnection.SourceIP) + } + + if originalConnection.SourcePort != remoteConnection.SourcePort { + t.Fail() + log.Printf("(in #%d) SourcePort's are not equal (orig: %d, unmsh: %d)", commandIndex, originalConnection.SourcePort, remoteConnection.SourcePort) + } + + if originalConnection.DestPort != remoteConnection.DestPort { + t.Fail() + log.Printf("(in #%d) DestPort's are not equal (orig: %d, unmsh: %d)", commandIndex, originalConnection.DestPort, remoteConnection.DestPort) + } + + if originalConnection.ClientIP != remoteConnection.ClientIP { + t.Fail() + log.Printf("(in #%d) ClientIP's are not equal (orig: %s, unmsh: %s)", commandIndex, originalConnection.ClientIP, remoteConnection.ClientIP) + } + + if originalConnection.ClientPort != remoteConnection.ClientPort { + t.Fail() + log.Printf("(in #%d) ClientPort's are not equal (orig: %d, unmsh: %d)", commandIndex, originalConnection.ClientPort, remoteConnection.ClientPort) + } + } +} + +func TestCheckClientParametersMarshalSupport(t *testing.T) { + commandInput := &CheckClientParameters{ + Type: "checkClientParameters", + SourceIP: "192.168.0.139", + SourcePort: 19132, + DestPort: 19132, + Protocol: "tcp", + } + + commandMarshalled, err := Marshal(commandInput.Type, commandInput) + + if err != nil { + t.Fatal(err.Error()) + } + + if logLevel == "debug" { + log.Printf("Generated array contents: %v", commandMarshalled) + } + + buf := bytes.NewBuffer(commandMarshalled) + commandType, commandUnmarshalledRaw, err := Unmarshal(buf) + + if err != nil { + t.Fatal(err.Error()) + } + + if commandType != commandInput.Type { + t.Fail() + log.Printf("command type does not match up! (orig: %s, unmsh: %s)", commandType, commandInput.Type) + } + + commandUnmarshalled, ok := commandUnmarshalledRaw.(*CheckClientParameters) + + if !ok { + t.Fatal("failed typecast") + } + + if commandInput.Type != commandUnmarshalled.Type { + t.Fail() + log.Printf("Types are not equal (orig: %s, unmsh: %s)", commandInput.Type, commandUnmarshalled.Type) + } + + if commandInput.SourceIP != commandUnmarshalled.SourceIP { + t.Fail() + log.Printf("SourceIP's are not equal (orig: %s, unmsh: %s)", commandInput.SourceIP, commandUnmarshalled.SourceIP) + } + + if commandInput.SourcePort != commandUnmarshalled.SourcePort { + t.Fail() + log.Printf("SourcePort's are not equal (orig: %d, unmsh: %d)", commandInput.SourcePort, commandUnmarshalled.SourcePort) + } + + if commandInput.DestPort != commandUnmarshalled.DestPort { + t.Fail() + log.Printf("DestPort's are not equal (orig: %d, unmsh: %d)", commandInput.DestPort, commandUnmarshalled.DestPort) + } + + if commandInput.Protocol != commandUnmarshalled.Protocol { + t.Fail() + log.Printf("Protocols are not equal (orig: %s, unmsh: %s)", commandInput.Protocol, commandUnmarshalled.Protocol) + } +} + +func TestCheckServerParametersMarshalSupport(t *testing.T) { + commandInput := &CheckServerParameters{ + Type: "checkServerParameters", + Arguments: []byte("Hello from automated testing"), + } + + commandMarshalled, err := Marshal(commandInput.Type, commandInput) + + if logLevel == "debug" { + log.Printf("Generated array contents: %v", commandMarshalled) + } + + if err != nil { + t.Fatal(err.Error()) + } + + buf := bytes.NewBuffer(commandMarshalled) + commandType, commandUnmarshalledRaw, err := Unmarshal(buf) + + if err != nil { + t.Fatal(err.Error()) + } + + if commandType != commandInput.Type { + t.Fail() + log.Print("command type does not match up!") + } + + commandUnmarshalled, ok := commandUnmarshalledRaw.(*CheckServerParameters) + + if !ok { + t.Fatal("failed typecast") + } + + if commandInput.Type != commandUnmarshalled.Type { + t.Fail() + log.Printf("Types are not equal (orig: %s, unmsh: %s)", commandInput.Type, commandUnmarshalled.Type) + } + + if !bytes.Equal(commandInput.Arguments, commandUnmarshalled.Arguments) { + log.Fatalf("Arguments are not equal (orig: '%s', unmsh: '%s')", string(commandInput.Arguments), string(commandUnmarshalled.Arguments)) + } +} + +func TestCheckParametersResponseMarshalSupport(t *testing.T) { + commandInput := &CheckParametersResponse{ + Type: "checkParametersResponse", + InResponseTo: "checkClientParameters", + IsValid: true, + Message: "Hello from automated testing", + } + + commandMarshalled, err := Marshal(commandInput.Type, commandInput) + + if err != nil { + t.Fatal(err.Error()) + } + + if logLevel == "debug" { + log.Printf("Generated array contents: %v", commandMarshalled) + } + + buf := bytes.NewBuffer(commandMarshalled) + commandType, commandUnmarshalledRaw, err := Unmarshal(buf) + + if err != nil { + t.Fatal(err.Error()) + } + + if commandType != commandInput.Type { + t.Fail() + log.Printf("command type does not match up! (orig: %s, unmsh: %s)", commandType, commandInput.Type) + } + + commandUnmarshalled, ok := commandUnmarshalledRaw.(*CheckParametersResponse) + + if !ok { + t.Fatal("failed typecast") + } + + if commandInput.Type != commandUnmarshalled.Type { + t.Fail() + log.Printf("Types are not equal (orig: %s, unmsh: %s)", commandInput.Type, commandUnmarshalled.Type) + } + + if commandInput.InResponseTo != commandUnmarshalled.InResponseTo { + t.Fail() + log.Printf("InResponseTo's are not equal (orig: %s, unmsh: %s)", commandInput.InResponseTo, commandUnmarshalled.InResponseTo) + } + + if commandInput.IsValid != commandUnmarshalled.IsValid { + t.Fail() + log.Printf("IsValid's are not equal (orig: %t, unmsh: %t)", commandInput.IsValid, commandUnmarshalled.IsValid) + } + + if commandInput.Message != commandUnmarshalled.Message { + t.Fail() + log.Printf("Messages are not equal (orig: %s, unmsh: %s)", commandInput.Message, commandUnmarshalled.Message) + } +} + +func TestBackendStatusRequestMarshalSupport(t *testing.T) { + commandInput := &BackendStatusRequest{ + Type: "backendStatusRequest", + } + + commandMarshalled, err := Marshal(commandInput.Type, commandInput) + + if logLevel == "debug" { + log.Printf("Generated array contents: %v", commandMarshalled) + } + + if err != nil { + t.Fatal(err.Error()) + } + + buf := bytes.NewBuffer(commandMarshalled) + commandType, commandUnmarshalledRaw, err := Unmarshal(buf) + + if err != nil { + t.Fatal(err.Error()) + } + + if commandType != commandInput.Type { + t.Fail() + log.Print("command type does not match up!") + } + + commandUnmarshalled, ok := commandUnmarshalledRaw.(*BackendStatusRequest) + + if !ok { + t.Fatal("failed typecast") + } + + if commandInput.Type != commandUnmarshalled.Type { + t.Fail() + log.Printf("Types are not equal (orig: %s, unmsh: %s)", commandInput.Type, commandUnmarshalled.Type) + } +} + +func TestBackendStatusResponseMarshalSupport(t *testing.T) { + commandInput := &BackendStatusResponse{ + Type: "backendStatusResponse", + IsRunning: true, + StatusCode: StatusFailure, + Message: "Hello from automated testing", + } + + commandMarshalled, err := Marshal(commandInput.Type, commandInput) + + if logLevel == "debug" { + log.Printf("Generated array contents: %v", commandMarshalled) + } + + if err != nil { + t.Fatal(err.Error()) + } + + buf := bytes.NewBuffer(commandMarshalled) + commandType, commandUnmarshalledRaw, err := Unmarshal(buf) + + if err != nil { + t.Fatal(err.Error()) + } + + if commandType != commandInput.Type { + t.Fail() + log.Print("command type does not match up!") + } + + commandUnmarshalled, ok := commandUnmarshalledRaw.(*BackendStatusResponse) + + if !ok { + t.Fatal("failed typecast") + } + + if commandInput.Type != commandUnmarshalled.Type { + t.Fail() + log.Printf("Types are not equal (orig: %s, unmsh: %s)", commandInput.Type, commandUnmarshalled.Type) + } + + if commandInput.IsRunning != commandUnmarshalled.IsRunning { + t.Fail() + log.Printf("IsRunning's are not equal (orig: %t, unmsh: %t)", commandInput.IsRunning, commandUnmarshalled.IsRunning) + } + + if commandInput.StatusCode != commandUnmarshalled.StatusCode { + t.Fail() + log.Printf("StatusCodes are not equal (orig: %d, unmsh: %d)", commandInput.StatusCode, commandUnmarshalled.StatusCode) + } + + if commandInput.Message != commandUnmarshalled.Message { + t.Fail() + log.Printf("Messages are not equal (orig: %s, unmsh: %s)", commandInput.Message, commandUnmarshalled.Message) + } +} + +func TestProxyStatusRequestMarshalSupport(t *testing.T) { + commandInput := &ProxyStatusRequest{ + Type: "proxyStatusRequest", + SourceIP: "192.168.0.139", + SourcePort: 19132, + DestPort: 19132, + Protocol: "tcp", + } + + commandMarshalled, err := Marshal(commandInput.Type, commandInput) + + if err != nil { + t.Fatal(err.Error()) + } + + if logLevel == "debug" { + log.Printf("Generated array contents: %v", commandMarshalled) + } + + buf := bytes.NewBuffer(commandMarshalled) + commandType, commandUnmarshalledRaw, err := Unmarshal(buf) + + if err != nil { + t.Fatal(err.Error()) + } + + if commandType != commandInput.Type { + t.Fail() + log.Print("command type does not match up!") + } + + commandUnmarshalled, ok := commandUnmarshalledRaw.(*ProxyStatusRequest) + + if !ok { + t.Fatal("failed typecast") + } + + if commandInput.Type != commandUnmarshalled.Type { + t.Fail() + log.Printf("Types are not equal (orig: %s, unmsh: %s)", commandInput.Type, commandUnmarshalled.Type) + } + + if commandInput.SourceIP != commandUnmarshalled.SourceIP { + t.Fail() + log.Printf("SourceIP's are not equal (orig: %s, unmsh: %s)", commandInput.SourceIP, commandUnmarshalled.SourceIP) + } + + if commandInput.SourcePort != commandUnmarshalled.SourcePort { + t.Fail() + log.Printf("SourcePort's are not equal (orig: %d, unmsh: %d)", commandInput.SourcePort, commandUnmarshalled.SourcePort) + } + + if commandInput.DestPort != commandUnmarshalled.DestPort { + t.Fail() + log.Printf("DestPort's are not equal (orig: %d, unmsh: %d)", commandInput.DestPort, commandUnmarshalled.DestPort) + } + + if commandInput.Protocol != commandUnmarshalled.Protocol { + t.Fail() + log.Printf("Protocols are not equal (orig: %s, unmsh: %s)", commandInput.Protocol, commandUnmarshalled.Protocol) + } +} + +func TestProxyStatusResponseMarshalSupport(t *testing.T) { + commandInput := &ProxyStatusResponse{ + Type: "proxyStatusResponse", + SourceIP: "192.168.0.139", + SourcePort: 19132, + DestPort: 19132, + Protocol: "tcp", + IsActive: true, + } + + commandMarshalled, err := Marshal(commandInput.Type, commandInput) + + if err != nil { + t.Fatal(err.Error()) + } + + if logLevel == "debug" { + log.Printf("Generated array contents: %v", commandMarshalled) + } + + buf := bytes.NewBuffer(commandMarshalled) + commandType, commandUnmarshalledRaw, err := Unmarshal(buf) + + if err != nil { + t.Fatal(err.Error()) + } + + if commandType != commandInput.Type { + t.Fail() + log.Print("command type does not match up!") + } + + commandUnmarshalled, ok := commandUnmarshalledRaw.(*ProxyStatusResponse) + + if !ok { + t.Fatal("failed typecast") + } + + if commandInput.Type != commandUnmarshalled.Type { + t.Fail() + log.Printf("Types are not equal (orig: %s, unmsh: %s)", commandInput.Type, commandUnmarshalled.Type) + } + + if commandInput.SourceIP != commandUnmarshalled.SourceIP { + t.Fail() + log.Printf("SourceIP's are not equal (orig: %s, unmsh: %s)", commandInput.SourceIP, commandUnmarshalled.SourceIP) + } + + if commandInput.SourcePort != commandUnmarshalled.SourcePort { + t.Fail() + log.Printf("SourcePort's are not equal (orig: %d, unmsh: %d)", commandInput.SourcePort, commandUnmarshalled.SourcePort) + } + + if commandInput.DestPort != commandUnmarshalled.DestPort { + t.Fail() + log.Printf("DestPort's are not equal (orig: %d, unmsh: %d)", commandInput.DestPort, commandUnmarshalled.DestPort) + } + + if commandInput.Protocol != commandUnmarshalled.Protocol { + t.Fail() + log.Printf("Protocols are not equal (orig: %s, unmsh: %s)", commandInput.Protocol, commandUnmarshalled.Protocol) + } + + if commandInput.IsActive != commandUnmarshalled.IsActive { + t.Fail() + log.Printf("IsActive's are not equal (orig: %t, unmsh: %t)", commandInput.IsActive, commandUnmarshalled.IsActive) + } +} + +func TestProxyConnectionRequestMarshalSupport(t *testing.T) { + commandInput := &ProxyInstanceRequest{ + Type: "proxyInstanceRequest", + } + + commandMarshalled, err := Marshal(commandInput.Type, commandInput) + + if logLevel == "debug" { + log.Printf("Generated array contents: %v", commandMarshalled) + } + + if err != nil { + t.Fatal(err.Error()) + } + + buf := bytes.NewBuffer(commandMarshalled) + commandType, commandUnmarshalledRaw, err := Unmarshal(buf) + + if err != nil { + t.Fatal(err.Error()) + } + + if commandType != commandInput.Type { + t.Fail() + log.Print("command type does not match up!") + } + + commandUnmarshalled, ok := commandUnmarshalledRaw.(*ProxyInstanceRequest) + + if !ok { + t.Fatal("failed typecast") + } + + if commandInput.Type != commandUnmarshalled.Type { + t.Fail() + log.Printf("Types are not equal (orig: %s, unmsh: %s)", commandInput.Type, commandUnmarshalled.Type) + } +} + +func TestProxyConnectionResponseMarshalSupport(t *testing.T) { + commandInput := &ProxyInstanceResponse{ + Type: "proxyInstanceResponse", + Proxies: []*ProxyInstance{ + { + SourceIP: "192.168.0.168", + SourcePort: 25565, + DestPort: 25565, + Protocol: "tcp", + }, + { + SourceIP: "127.0.0.1", + SourcePort: 19132, + DestPort: 19132, + Protocol: "udp", + }, + { + SourceIP: "68.42.203.47", + SourcePort: 22, + DestPort: 2222, + Protocol: "tcp", + }, + }, + } + + commandMarshalled, err := Marshal(commandInput.Type, commandInput) + + if err != nil { + t.Fatal(err.Error()) + } + + if logLevel == "debug" { + log.Printf("Generated array contents: %v", commandMarshalled) + } + + buf := bytes.NewBuffer(commandMarshalled) + commandType, commandUnmarshalledRaw, err := Unmarshal(buf) + + if err != nil { + t.Fatal(err.Error()) + } + + if commandType != commandInput.Type { + t.Fail() + log.Print("command type does not match up!") + } + + commandUnmarshalled, ok := commandUnmarshalledRaw.(*ProxyInstanceResponse) + + if !ok { + t.Fatal("failed typecast") + } + + if commandInput.Type != commandUnmarshalled.Type { + t.Fail() + log.Printf("Types are not equal (orig: %s, unmsh: %s)", commandInput.Type, commandUnmarshalled.Type) + } + + for proxyIndex, originalProxy := range commandInput.Proxies { + remoteProxy := commandUnmarshalled.Proxies[proxyIndex] + + if originalProxy.SourceIP != remoteProxy.SourceIP { + t.Fail() + log.Printf("(in #%d) SourceIP's are not equal (orig: %s, unmsh: %s)", proxyIndex, originalProxy.SourceIP, remoteProxy.SourceIP) + } + + if originalProxy.SourcePort != remoteProxy.SourcePort { + t.Fail() + log.Printf("(in #%d) SourcePort's are not equal (orig: %d, unmsh: %d)", proxyIndex, originalProxy.SourcePort, remoteProxy.SourcePort) + } + + if originalProxy.DestPort != remoteProxy.DestPort { + t.Fail() + log.Printf("(in #%d) DestPort's are not equal (orig: %d, unmsh: %d)", proxyIndex, originalProxy.DestPort, remoteProxy.DestPort) + } + + if originalProxy.Protocol != remoteProxy.Protocol { + t.Fail() + log.Printf("(in #%d) ClientIP's are not equal (orig: %s, unmsh: %s)", proxyIndex, originalProxy.Protocol, remoteProxy.Protocol) + } + } +} diff --git a/backend/commonbackend/unmarshal.go b/backend/commonbackend/unmarshal.go new file mode 100644 index 0000000..b8500dd --- /dev/null +++ b/backend/commonbackend/unmarshal.go @@ -0,0 +1,665 @@ +package commonbackend + +import ( + "encoding/binary" + "fmt" + "io" + "net" +) + +func unmarshalIndividualConnectionStruct(conn io.Reader) (*ProxyClientConnection, error) { + serverIPVersion := make([]byte, 1) + + if _, err := conn.Read(serverIPVersion); err != nil { + return nil, fmt.Errorf("couldn't read server IP version") + } + + var serverIPSize uint8 + + if serverIPVersion[0] == 4 { + serverIPSize = IPv4Size + } else if serverIPVersion[0] == 6 { + serverIPSize = IPv6Size + } else if serverIPVersion[0] == '\n' { + return nil, fmt.Errorf("no data found") + } else { + return nil, fmt.Errorf("invalid server IP version recieved") + } + + serverIP := make(net.IP, serverIPSize) + + if _, err := conn.Read(serverIP); err != nil { + return nil, fmt.Errorf("couldn't read server IP") + } + + sourcePort := make([]byte, 2) + + if _, err := conn.Read(sourcePort); err != nil { + return nil, fmt.Errorf("couldn't read source port") + } + + destinationPort := make([]byte, 2) + + if _, err := conn.Read(destinationPort); err != nil { + return nil, fmt.Errorf("couldn't read source port") + } + + clientIPVersion := make([]byte, 1) + + if _, err := conn.Read(clientIPVersion); err != nil { + return nil, fmt.Errorf("couldn't read server IP version") + } + + var clientIPSize uint8 + + if clientIPVersion[0] == 4 { + clientIPSize = IPv4Size + } else if clientIPVersion[0] == 6 { + clientIPSize = IPv6Size + } else { + return nil, fmt.Errorf("invalid server IP version recieved") + } + + clientIP := make(net.IP, clientIPSize) + + if _, err := conn.Read(clientIP); err != nil { + return nil, fmt.Errorf("couldn't read server IP") + } + + clientPort := make([]byte, 2) + + if _, err := conn.Read(clientPort); err != nil { + return nil, fmt.Errorf("couldn't read source port") + } + + return &ProxyClientConnection{ + SourceIP: serverIP.String(), + SourcePort: binary.BigEndian.Uint16(sourcePort), + DestPort: binary.BigEndian.Uint16(destinationPort), + ClientIP: clientIP.String(), + ClientPort: binary.BigEndian.Uint16(clientPort), + }, nil +} + +func unmarshalIndividualProxyStruct(conn io.Reader) (*ProxyInstance, error) { + ipVersion := make([]byte, 1) + + if _, err := conn.Read(ipVersion); err != nil { + return nil, fmt.Errorf("couldn't read ip version") + } + + var ipSize uint8 + + if ipVersion[0] == 4 { + ipSize = IPv4Size + } else if ipVersion[0] == 6 { + ipSize = IPv6Size + } else if ipVersion[0] == '\n' { + return nil, fmt.Errorf("no data found") + } else { + return nil, fmt.Errorf("invalid IP version recieved") + } + + ip := make(net.IP, ipSize) + + if _, err := conn.Read(ip); err != nil { + return nil, fmt.Errorf("couldn't read source IP") + } + + sourcePort := make([]byte, 2) + + if _, err := conn.Read(sourcePort); err != nil { + return nil, fmt.Errorf("couldn't read source port") + } + + destPort := make([]byte, 2) + + if _, err := conn.Read(destPort); err != nil { + return nil, fmt.Errorf("couldn't read destination port") + } + + protocolBytes := make([]byte, 1) + + if _, err := conn.Read(protocolBytes); err != nil { + return nil, fmt.Errorf("couldn't read protocol") + } + + var protocol string + + if protocolBytes[0] == TCP { + protocol = "tcp" + } else if protocolBytes[0] == UDP { + protocol = "udp" + } else { + return nil, fmt.Errorf("invalid protocol") + } + + return &ProxyInstance{ + SourceIP: ip.String(), + SourcePort: binary.BigEndian.Uint16(sourcePort), + DestPort: binary.BigEndian.Uint16(destPort), + Protocol: protocol, + }, nil +} + +func Unmarshal(conn io.Reader) (string, interface{}, error) { + commandType := make([]byte, 1) + + if _, err := conn.Read(commandType); err != nil { + return "", nil, fmt.Errorf("couldn't read command") + } + + switch commandType[0] { + case StartID: + argumentsLength := make([]byte, 2) + + if _, err := conn.Read(argumentsLength); err != nil { + return "", nil, fmt.Errorf("couldn't read argument length") + } + + arguments := make([]byte, binary.BigEndian.Uint16(argumentsLength)) + + if _, err := conn.Read(arguments); err != nil { + return "", nil, fmt.Errorf("couldn't read arguments") + } + + return "start", &Start{ + Type: "start", + Arguments: arguments, + }, nil + case StopID: + return "stop", &Stop{ + Type: "stop", + }, nil + case AddProxyID: + ipVersion := make([]byte, 1) + + if _, err := conn.Read(ipVersion); err != nil { + return "", nil, fmt.Errorf("couldn't read ip version") + } + + var ipSize uint8 + + if ipVersion[0] == 4 { + ipSize = IPv4Size + } else if ipVersion[0] == 6 { + ipSize = IPv6Size + } else { + return "", nil, fmt.Errorf("invalid IP version recieved") + } + + ip := make(net.IP, ipSize) + + if _, err := conn.Read(ip); err != nil { + return "", nil, fmt.Errorf("couldn't read source IP") + } + + sourcePort := make([]byte, 2) + + if _, err := conn.Read(sourcePort); err != nil { + return "", nil, fmt.Errorf("couldn't read source port") + } + + destPort := make([]byte, 2) + + if _, err := conn.Read(destPort); err != nil { + return "", nil, fmt.Errorf("couldn't read destination port") + } + + protocolBytes := make([]byte, 1) + + if _, err := conn.Read(protocolBytes); err != nil { + return "", nil, fmt.Errorf("couldn't read protocol") + } + + var protocol string + + if protocolBytes[0] == TCP { + protocol = "tcp" + } else if protocolBytes[1] == UDP { + protocol = "udp" + } else { + return "", nil, fmt.Errorf("invalid protocol") + } + + return "addProxy", &AddProxy{ + Type: "addProxy", + SourceIP: ip.String(), + SourcePort: binary.BigEndian.Uint16(sourcePort), + DestPort: binary.BigEndian.Uint16(destPort), + Protocol: protocol, + }, nil + case RemoveProxyID: + ipVersion := make([]byte, 1) + + if _, err := conn.Read(ipVersion); err != nil { + return "", nil, fmt.Errorf("couldn't read ip version") + } + + var ipSize uint8 + + if ipVersion[0] == 4 { + ipSize = IPv4Size + } else if ipVersion[0] == 6 { + ipSize = IPv6Size + } else { + return "", nil, fmt.Errorf("invalid IP version recieved") + } + + ip := make(net.IP, ipSize) + + if _, err := conn.Read(ip); err != nil { + return "", nil, fmt.Errorf("couldn't read source IP") + } + + sourcePort := make([]byte, 2) + + if _, err := conn.Read(sourcePort); err != nil { + return "", nil, fmt.Errorf("couldn't read source port") + } + + destPort := make([]byte, 2) + + if _, err := conn.Read(destPort); err != nil { + return "", nil, fmt.Errorf("couldn't read destination port") + } + + protocolBytes := make([]byte, 1) + + if _, err := conn.Read(protocolBytes); err != nil { + return "", nil, fmt.Errorf("couldn't read protocol") + } + + var protocol string + + if protocolBytes[0] == TCP { + protocol = "tcp" + } else if protocolBytes[1] == UDP { + protocol = "udp" + } else { + return "", nil, fmt.Errorf("invalid protocol") + } + + return "removeProxy", &RemoveProxy{ + Type: "removeProxy", + SourceIP: ip.String(), + SourcePort: binary.BigEndian.Uint16(sourcePort), + DestPort: binary.BigEndian.Uint16(destPort), + Protocol: protocol, + }, nil + case ProxyConnectionsResponseID: + connections := []*ProxyClientConnection{} + delimiter := make([]byte, 1) + var errorReturn error + + // Infinite loop because we don't know the length + for { + connection, err := unmarshalIndividualConnectionStruct(conn) + + if err != nil { + if err.Error() == "no data found" { + break + } + + return "", nil, err + } + + connections = append(connections, connection) + + if _, err := conn.Read(delimiter); err != nil { + return "", nil, fmt.Errorf("couldn't read delimiter") + } + + if delimiter[0] == '\r' { + continue + } else if delimiter[0] == '\n' { + break + } else { + // WTF? This shouldn't happen. Break out and return, but give an error + errorReturn = fmt.Errorf("invalid delimiter recieved while processing stream") + break + } + } + + return "proxyConnectionsResponse", &ProxyConnectionsResponse{ + Type: "proxyConnectionsResponse", + Connections: connections, + }, errorReturn + case CheckClientParametersID: + ipVersion := make([]byte, 1) + + if _, err := conn.Read(ipVersion); err != nil { + return "", nil, fmt.Errorf("couldn't read ip version") + } + + var ipSize uint8 + + if ipVersion[0] == 4 { + ipSize = IPv4Size + } else if ipVersion[0] == 6 { + ipSize = IPv6Size + } else { + return "", nil, fmt.Errorf("invalid IP version recieved") + } + + ip := make(net.IP, ipSize) + + if _, err := conn.Read(ip); err != nil { + return "", nil, fmt.Errorf("couldn't read source IP") + } + + sourcePort := make([]byte, 2) + + if _, err := conn.Read(sourcePort); err != nil { + return "", nil, fmt.Errorf("couldn't read source port") + } + + destPort := make([]byte, 2) + + if _, err := conn.Read(destPort); err != nil { + return "", nil, fmt.Errorf("couldn't read destination port") + } + + protocolBytes := make([]byte, 1) + + if _, err := conn.Read(protocolBytes); err != nil { + return "", nil, fmt.Errorf("couldn't read protocol") + } + + var protocol string + + if protocolBytes[0] == TCP { + protocol = "tcp" + } else if protocolBytes[1] == UDP { + protocol = "udp" + } else { + return "", nil, fmt.Errorf("invalid protocol") + } + + return "checkClientParameters", &CheckClientParameters{ + Type: "checkClientParameters", + SourceIP: ip.String(), + SourcePort: binary.BigEndian.Uint16(sourcePort), + DestPort: binary.BigEndian.Uint16(destPort), + Protocol: protocol, + }, nil + case CheckServerParametersID: + argumentsLength := make([]byte, 2) + + if _, err := conn.Read(argumentsLength); err != nil { + return "", nil, fmt.Errorf("couldn't read argument length") + } + + arguments := make([]byte, binary.BigEndian.Uint16(argumentsLength)) + + if _, err := conn.Read(arguments); err != nil { + return "", nil, fmt.Errorf("couldn't read arguments") + } + + return "checkServerParameters", &CheckServerParameters{ + Type: "checkServerParameters", + Arguments: arguments, + }, nil + case CheckParametersResponseID: + checkMethodByte := make([]byte, 1) + + if _, err := conn.Read(checkMethodByte); err != nil { + return "", nil, fmt.Errorf("couldn't read check method byte") + } + + var checkMethod string + + if checkMethodByte[0] == CheckClientParametersID { + checkMethod = "checkClientParameters" + } else if checkMethodByte[0] == CheckServerParametersID { + checkMethod = "checkServerParameters" + } else { + return "", nil, fmt.Errorf("invalid check method recieved") + } + + isValid := make([]byte, 1) + + if _, err := conn.Read(isValid); err != nil { + return "", nil, fmt.Errorf("couldn't read isValid byte") + } + + messageLengthBytes := make([]byte, 2) + + if _, err := conn.Read(messageLengthBytes); err != nil { + return "", nil, fmt.Errorf("couldn't read message length") + } + + messageLength := binary.BigEndian.Uint16(messageLengthBytes) + var message string + + if messageLength != 0 { + messageBytes := make([]byte, messageLength) + + if _, err := conn.Read(messageBytes); err != nil { + return "", nil, fmt.Errorf("couldn't read message") + } + + message = string(messageBytes) + } + + return "checkParametersResponse", &CheckParametersResponse{ + Type: "checkParametersResponse", + InResponseTo: checkMethod, + IsValid: isValid[0] == 1, + Message: message, + }, nil + case BackendStatusResponseID: + isRunning := make([]byte, 1) + + if _, err := conn.Read(isRunning); err != nil { + return "", nil, fmt.Errorf("couldn't read isRunning field") + } + + statusCode := make([]byte, 1) + + if _, err := conn.Read(statusCode); err != nil { + return "", nil, fmt.Errorf("couldn't read status code field") + } + + messageLengthBytes := make([]byte, 2) + + if _, err := conn.Read(messageLengthBytes); err != nil { + return "", nil, fmt.Errorf("couldn't read message length") + } + + messageLength := binary.BigEndian.Uint16(messageLengthBytes) + var message string + + if messageLength != 0 { + messageBytes := make([]byte, messageLength) + + if _, err := conn.Read(messageBytes); err != nil { + return "", nil, fmt.Errorf("couldn't read message") + } + + message = string(messageBytes) + } + + return "backendStatusResponse", &BackendStatusResponse{ + Type: "backendStatusResponse", + IsRunning: isRunning[0] == 1, + StatusCode: int(statusCode[0]), + Message: message, + }, nil + case BackendStatusRequestID: + return "backendStatusRequest", &BackendStatusRequest{ + Type: "backendStatusRequest", + }, nil + case ProxyStatusRequestID: + ipVersion := make([]byte, 1) + + if _, err := conn.Read(ipVersion); err != nil { + return "", nil, fmt.Errorf("couldn't read ip version") + } + + var ipSize uint8 + + if ipVersion[0] == 4 { + ipSize = IPv4Size + } else if ipVersion[0] == 6 { + ipSize = IPv6Size + } else { + return "", nil, fmt.Errorf("invalid IP version recieved") + } + + ip := make(net.IP, ipSize) + + if _, err := conn.Read(ip); err != nil { + return "", nil, fmt.Errorf("couldn't read source IP") + } + + sourcePort := make([]byte, 2) + + if _, err := conn.Read(sourcePort); err != nil { + return "", nil, fmt.Errorf("couldn't read source port") + } + + destPort := make([]byte, 2) + + if _, err := conn.Read(destPort); err != nil { + return "", nil, fmt.Errorf("couldn't read destination port") + } + + protocolBytes := make([]byte, 1) + + if _, err := conn.Read(protocolBytes); err != nil { + return "", nil, fmt.Errorf("couldn't read protocol") + } + + var protocol string + + if protocolBytes[0] == TCP { + protocol = "tcp" + } else if protocolBytes[1] == UDP { + protocol = "udp" + } else { + return "", nil, fmt.Errorf("invalid protocol") + } + + return "proxyStatusRequest", &ProxyStatusRequest{ + Type: "proxyStatusRequest", + SourceIP: ip.String(), + SourcePort: binary.BigEndian.Uint16(sourcePort), + DestPort: binary.BigEndian.Uint16(destPort), + Protocol: protocol, + }, nil + case ProxyStatusResponseID: + ipVersion := make([]byte, 1) + + if _, err := conn.Read(ipVersion); err != nil { + return "", nil, fmt.Errorf("couldn't read ip version") + } + + var ipSize uint8 + + if ipVersion[0] == 4 { + ipSize = IPv4Size + } else if ipVersion[0] == 6 { + ipSize = IPv6Size + } else { + return "", nil, fmt.Errorf("invalid IP version recieved") + } + + ip := make(net.IP, ipSize) + + if _, err := conn.Read(ip); err != nil { + return "", nil, fmt.Errorf("couldn't read source IP") + } + + sourcePort := make([]byte, 2) + + if _, err := conn.Read(sourcePort); err != nil { + return "", nil, fmt.Errorf("couldn't read source port") + } + + destPort := make([]byte, 2) + + if _, err := conn.Read(destPort); err != nil { + return "", nil, fmt.Errorf("couldn't read destination port") + } + + protocolBytes := make([]byte, 1) + + if _, err := conn.Read(protocolBytes); err != nil { + return "", nil, fmt.Errorf("couldn't read protocol") + } + + var protocol string + + if protocolBytes[0] == TCP { + protocol = "tcp" + } else if protocolBytes[1] == UDP { + protocol = "udp" + } else { + return "", nil, fmt.Errorf("invalid protocol") + } + + isActive := make([]byte, 1) + + if _, err := conn.Read(isActive); err != nil { + return "", nil, fmt.Errorf("couldn't read isActive field") + } + + return "proxyStatusResponse", &ProxyStatusResponse{ + Type: "proxyStatusResponse", + SourceIP: ip.String(), + SourcePort: binary.BigEndian.Uint16(sourcePort), + DestPort: binary.BigEndian.Uint16(destPort), + Protocol: protocol, + IsActive: isActive[0] == 1, + }, nil + case ProxyInstanceRequestID: + return "proxyInstanceRequest", &ProxyInstanceRequest{ + Type: "proxyInstanceRequest", + }, nil + case ProxyInstanceResponseID: + proxies := []*ProxyInstance{} + delimiter := make([]byte, 1) + var errorReturn error + + // Infinite loop because we don't know the length + for { + proxy, err := unmarshalIndividualProxyStruct(conn) + + if err != nil { + if err.Error() == "no data found" { + break + } + + return "", nil, err + } + + proxies = append(proxies, proxy) + + if _, err := conn.Read(delimiter); err != nil { + return "", nil, fmt.Errorf("couldn't read delimiter") + } + + if delimiter[0] == '\r' { + continue + } else if delimiter[0] == '\n' { + break + } else { + // WTF? This shouldn't happen. Break out and return, but give an error + errorReturn = fmt.Errorf("invalid delimiter recieved while processing stream") + break + } + } + + return "proxyInstanceResponse", &ProxyInstanceResponse{ + Type: "proxyInstanceResponse", + Proxies: proxies, + }, errorReturn + case ProxyConnectionsRequestID: + return "proxyConnectionsRequest", &ProxyConnectionsRequest{ + Type: "proxyConnectionsRequest", + }, nil + } + + return "", nil, fmt.Errorf("couldn't match command ID") +}