From ca41423aa6e441cd3cdddda31ce81bde078fd534 Mon Sep 17 00:00:00 2001 From: Peter Kurfer Date: Sat, 13 Feb 2021 13:59:29 +0000 Subject: [PATCH 1/6] Initial commit --- README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..cd7e63d --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# client-dotnet + From cdcce2ec26b7439ba046ad909f9b9a5b057ad281 Mon Sep 17 00:00:00 2001 From: Peter Kurfer Date: Sat, 13 Feb 2021 18:20:57 +0100 Subject: [PATCH 2/6] Initial commit --- .editorconfig | 10 +++++ .gitignore | 2 + README.md | 1 + proto/audit/details/dns_details.proto | 42 +++++++++++++++++++++ proto/audit/details/http_details.proto | 32 ++++++++++++++++ proto/audit/event_entity.proto | 51 +++++++++++++++++++++++++ proto/rpc/audit.proto | 52 ++++++++++++++++++++++++++ proto/rpc/health.proto | 34 +++++++++++++++++ 8 files changed, 224 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 README.md create mode 100644 proto/audit/details/dns_details.proto create mode 100644 proto/audit/details/http_details.proto create mode 100644 proto/audit/event_entity.proto create mode 100644 proto/rpc/audit.proto create mode 100644 proto/rpc/health.proto diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..698c63d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 + +[*.proto] +indent_style = space +indent_size = 4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1c0b8d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode/ +.idea/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..19fee22 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# INetMock protobuf API \ No newline at end of file diff --git a/proto/audit/details/dns_details.proto b/proto/audit/details/dns_details.proto new file mode 100644 index 0000000..3dc2fb8 --- /dev/null +++ b/proto/audit/details/dns_details.proto @@ -0,0 +1,42 @@ +syntax = "proto3"; + +option go_package = "gitlab.com/inetmock/inetmock/pkg/audit/details"; +option java_multiple_files = true; +option java_package = "com.github.baez90.inetmock.audit.details"; +option java_outer_classname = "HandlerEventProto"; + +package inetmock.audit.details; + +enum DNSOpCode { + Query = 0; + Status = 2; + Notify = 4; + Update = 5; +} + +enum ResourceRecordType { + UnknownRR = 0; + A = 1; + NS = 2; + CNAME = 5; + SOA = 6; + PTR = 12; + HINFO = 13; + MINFO = 14; + MX = 15; + TXT = 16; + RP = 17; + AAAA = 28; + SRV = 33; + NAPTR = 35; +} + +message DNSQuestionEntity { + ResourceRecordType type = 1; + string name = 2; +} + +message DNSDetailsEntity { + DNSOpCode opcode = 1; + repeated DNSQuestionEntity questions = 2; +} diff --git a/proto/audit/details/http_details.proto b/proto/audit/details/http_details.proto new file mode 100644 index 0000000..e9a3955 --- /dev/null +++ b/proto/audit/details/http_details.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; + +option go_package = "gitlab.com/inetmock/inetmock/pkg/audit/details"; +option java_multiple_files = true; +option java_package = "com.github.baez90.inetmock.audit.details"; +option java_outer_classname = "HandlerEventProto"; + +package inetmock.audit.details; + +enum HTTPMethod { + GET = 0; + HEAD = 1; + POST = 2; + PUT = 3; + DELETE = 4; + CONNECT = 5; + OPTIONS = 6; + TRACE = 7; + PATCH = 8; +} + +message HTTPHeaderValue { + repeated string values = 1; +} + +message HTTPDetailsEntity { + HTTPMethod method = 1; + string host = 2; + string uri = 3; + string proto = 4; + map headers = 5; +} \ No newline at end of file diff --git a/proto/audit/event_entity.proto b/proto/audit/event_entity.proto new file mode 100644 index 0000000..00492af --- /dev/null +++ b/proto/audit/event_entity.proto @@ -0,0 +1,51 @@ +syntax = "proto3"; + +option go_package = "gitlab.com/inetmock/inetmock/pkg/audit"; +option java_multiple_files = true; +option java_package = "com.github.baez90.inetmock.audit"; +option java_outer_classname = "HandlerEventProto"; + +package inetmock.audit; + +import "google/protobuf/any.proto"; +import "google/protobuf/timestamp.proto"; + +enum TransportProtocol { + UNKNOWN_TRANSPORT = 0; + TCP = 1; + UDP = 2; +} + +enum AppProtocol { + UNKNOWN_APPLICATION = 0; + DNS = 1; + HTTP = 2; + HTTP_PROXY = 3; +} + +enum TLSVersion { + SSLv30 = 0; + TLS10 = 1; + TLS11 = 2; + TLS12 = 3; + TLS13 = 4; +} + +message TLSDetailsEntity { + TLSVersion version = 1; + string cipherSuite = 2; + string serverName = 3; +} + +message EventEntity { + int64 id = 1; + google.protobuf.Timestamp timestamp = 2; + TransportProtocol transport = 3; + AppProtocol application = 4; + bytes sourceIP = 5; + bytes destinationIP = 6; + uint32 sourcePort = 7; + uint32 destinationPort = 8; + TLSDetailsEntity tls = 9; + google.protobuf.Any protocolDetails = 10; +} \ No newline at end of file diff --git a/proto/rpc/audit.proto b/proto/rpc/audit.proto new file mode 100644 index 0000000..1d43bd3 --- /dev/null +++ b/proto/rpc/audit.proto @@ -0,0 +1,52 @@ +syntax = "proto3"; + +option go_package = "gitlab.com/inetmock/inetmock/pkg/rpc"; +option java_multiple_files = true; +option java_package = "com.github.baez90.inetmock.rpc"; +option java_outer_classname = "AuditProto"; + +import 'pkg/audit/event_entity.proto'; + +package inetmock.rpc; + +enum FileOpenMode { + TRUNCATE = 0; + APPEND = 1; +} + +message WatchEventsRequest { + string watcherName = 1; +} + +message RegisterFileSinkRequest { + string targetPath = 1; + FileOpenMode openMode = 2; + uint32 permissions = 3; +} + +message RegisterFileSinkResponse { + +} + +message RemoveFileSinkRequest { + string targetPath = 1; +} + +message RemoveFileSinkResponse { + bool SinkGotRemoved = 1; +} + +message ListSinksRequest { + +} + +message ListSinksResponse { + repeated string sinks = 1; +} + +service Audit { + rpc WatchEvents (WatchEventsRequest) returns (stream inetmock.audit.EventEntity); + rpc RegisterFileSink (RegisterFileSinkRequest) returns (RegisterFileSinkResponse); + rpc RemoveFileSink (RemoveFileSinkRequest) returns (RemoveFileSinkResponse); + rpc ListSinks(ListSinksRequest) returns (ListSinksResponse); +} \ No newline at end of file diff --git a/proto/rpc/health.proto b/proto/rpc/health.proto new file mode 100644 index 0000000..3ff1aee --- /dev/null +++ b/proto/rpc/health.proto @@ -0,0 +1,34 @@ +syntax = "proto3"; + +option go_package = "gitlab.com/inetmock/inetmock/internal/rpc"; +option java_multiple_files = true; +option java_package = "com.github.baez90.inetmock.rpc"; +option java_outer_classname = "HealthProto"; + +package inetmock.rpc; + +service Health { + rpc GetHealth (HealthRequest) returns (HealthResponse) { + } +} + +enum HealthState { + HEALTHY = 0; + INITIALIZING = 1; + UNHEALTHY = 2; + UNKNOWN = 3; +} + +message HealthRequest { + repeated string components = 1; +} + +message ComponentHealth { + HealthState State = 1; + string message = 2; +} + +message HealthResponse { + HealthState overallHealthState = 1; + map componentsHealth = 2; +} \ No newline at end of file From e6959915c68e24506e55277cf783d47e867064bc Mon Sep 17 00:00:00 2001 From: Peter Kurfer Date: Mon, 15 Feb 2021 08:45:34 +0100 Subject: [PATCH 3/6] Restructure and extend protos for easier usage --- api/.editorconfig | 10 ++++ api/.gitignore | 2 + api/README.md | 1 + api/proto/audit/details/dns_details.proto | 43 ++++++++++++++++++ api/proto/audit/details/http_details.proto | 33 ++++++++++++++ api/proto/audit/event_entity.proto | 52 +++++++++++++++++++++ api/proto/rpc/audit.proto | 53 ++++++++++++++++++++++ api/proto/rpc/health.proto | 35 ++++++++++++++ 8 files changed, 229 insertions(+) create mode 100644 api/.editorconfig create mode 100644 api/.gitignore create mode 100644 api/README.md create mode 100644 api/proto/audit/details/dns_details.proto create mode 100644 api/proto/audit/details/http_details.proto create mode 100644 api/proto/audit/event_entity.proto create mode 100644 api/proto/rpc/audit.proto create mode 100644 api/proto/rpc/health.proto diff --git a/api/.editorconfig b/api/.editorconfig new file mode 100644 index 0000000..698c63d --- /dev/null +++ b/api/.editorconfig @@ -0,0 +1,10 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 + +[*.proto] +indent_style = space +indent_size = 4 diff --git a/api/.gitignore b/api/.gitignore new file mode 100644 index 0000000..1c0b8d4 --- /dev/null +++ b/api/.gitignore @@ -0,0 +1,2 @@ +.vscode/ +.idea/ \ No newline at end of file diff --git a/api/README.md b/api/README.md new file mode 100644 index 0000000..19fee22 --- /dev/null +++ b/api/README.md @@ -0,0 +1 @@ +# INetMock protobuf API \ No newline at end of file diff --git a/api/proto/audit/details/dns_details.proto b/api/proto/audit/details/dns_details.proto new file mode 100644 index 0000000..f820840 --- /dev/null +++ b/api/proto/audit/details/dns_details.proto @@ -0,0 +1,43 @@ +syntax = "proto3"; + +option go_package = "gitlab.com/inetmock/inetmock/pkg/audit/details"; +option java_multiple_files = true; +option java_package = "com.github.baez90.inetmock.audit.details"; +option java_outer_classname = "HandlerEventProto"; +option csharp_namespace = "INetMock.Client.Audit.Details"; + +package inetmock.audit.details; + +enum DNSOpCode { + Query = 0; + Status = 2; + Notify = 4; + Update = 5; +} + +enum ResourceRecordType { + UnknownRR = 0; + A = 1; + NS = 2; + CNAME = 5; + SOA = 6; + PTR = 12; + HINFO = 13; + MINFO = 14; + MX = 15; + TXT = 16; + RP = 17; + AAAA = 28; + SRV = 33; + NAPTR = 35; +} + +message DNSQuestionEntity { + ResourceRecordType type = 1; + string name = 2; +} + +message DNSDetailsEntity { + DNSOpCode opcode = 1; + repeated DNSQuestionEntity questions = 2; +} diff --git a/api/proto/audit/details/http_details.proto b/api/proto/audit/details/http_details.proto new file mode 100644 index 0000000..e5e5d7a --- /dev/null +++ b/api/proto/audit/details/http_details.proto @@ -0,0 +1,33 @@ +syntax = "proto3"; + +option go_package = "gitlab.com/inetmock/inetmock/pkg/audit/details"; +option java_multiple_files = true; +option java_package = "com.github.baez90.inetmock.audit.details"; +option java_outer_classname = "HandlerEventProto"; +option csharp_namespace = "INetMock.Client.Audit.Details"; + +package inetmock.audit.details; + +enum HTTPMethod { + GET = 0; + HEAD = 1; + POST = 2; + PUT = 3; + DELETE = 4; + CONNECT = 5; + OPTIONS = 6; + TRACE = 7; + PATCH = 8; +} + +message HTTPHeaderValue { + repeated string values = 1; +} + +message HTTPDetailsEntity { + HTTPMethod method = 1; + string host = 2; + string uri = 3; + string proto = 4; + map headers = 5; +} diff --git a/api/proto/audit/event_entity.proto b/api/proto/audit/event_entity.proto new file mode 100644 index 0000000..3b213ee --- /dev/null +++ b/api/proto/audit/event_entity.proto @@ -0,0 +1,52 @@ +syntax = "proto3"; + +option go_package = "gitlab.com/inetmock/inetmock/pkg/audit"; +option java_multiple_files = true; +option java_package = "com.github.baez90.inetmock.audit"; +option java_outer_classname = "HandlerEventProto"; +option csharp_namespace = "INetMock.Client.Audit"; + +package inetmock.audit; + +import "google/protobuf/any.proto"; +import "google/protobuf/timestamp.proto"; + +enum TransportProtocol { + UNKNOWN_TRANSPORT = 0; + TCP = 1; + UDP = 2; +} + +enum AppProtocol { + UNKNOWN_APPLICATION = 0; + DNS = 1; + HTTP = 2; + HTTP_PROXY = 3; +} + +enum TLSVersion { + SSLv30 = 0; + TLS10 = 1; + TLS11 = 2; + TLS12 = 3; + TLS13 = 4; +} + +message TLSDetailsEntity { + TLSVersion version = 1; + string cipherSuite = 2; + string serverName = 3; +} + +message EventEntity { + int64 id = 1; + google.protobuf.Timestamp timestamp = 2; + TransportProtocol transport = 3; + AppProtocol application = 4; + bytes sourceIP = 5; + bytes destinationIP = 6; + uint32 sourcePort = 7; + uint32 destinationPort = 8; + TLSDetailsEntity tls = 9; + google.protobuf.Any protocolDetails = 10; +} diff --git a/api/proto/rpc/audit.proto b/api/proto/rpc/audit.proto new file mode 100644 index 0000000..ca369c1 --- /dev/null +++ b/api/proto/rpc/audit.proto @@ -0,0 +1,53 @@ +syntax = "proto3"; + +option go_package = "gitlab.com/inetmock/inetmock/pkg/rpc"; +option java_multiple_files = true; +option java_package = "com.github.baez90.inetmock.rpc"; +option java_outer_classname = "AuditProto"; +option csharp_namespace = "INetMock.Client.Rpc"; + +import 'proto/audit/event_entity.proto'; + +package inetmock.rpc; + +enum FileOpenMode { + TRUNCATE = 0; + APPEND = 1; +} + +message WatchEventsRequest { + string watcherName = 1; +} + +message RegisterFileSinkRequest { + string targetPath = 1; + FileOpenMode openMode = 2; + uint32 permissions = 3; +} + +message RegisterFileSinkResponse { + +} + +message RemoveFileSinkRequest { + string targetPath = 1; +} + +message RemoveFileSinkResponse { + bool SinkGotRemoved = 1; +} + +message ListSinksRequest { + +} + +message ListSinksResponse { + repeated string sinks = 1; +} + +service Audit { + rpc WatchEvents (WatchEventsRequest) returns (stream inetmock.audit.EventEntity); + rpc RegisterFileSink (RegisterFileSinkRequest) returns (RegisterFileSinkResponse); + rpc RemoveFileSink (RemoveFileSinkRequest) returns (RemoveFileSinkResponse); + rpc ListSinks(ListSinksRequest) returns (ListSinksResponse); +} diff --git a/api/proto/rpc/health.proto b/api/proto/rpc/health.proto new file mode 100644 index 0000000..193bc8c --- /dev/null +++ b/api/proto/rpc/health.proto @@ -0,0 +1,35 @@ +syntax = "proto3"; + +option go_package = "gitlab.com/inetmock/inetmock/internal/rpc"; +option java_multiple_files = true; +option java_package = "com.github.baez90.inetmock.rpc"; +option java_outer_classname = "HealthProto"; +option csharp_namespace = "INetMock.Client.Rpc"; + +package inetmock.rpc; + +service Health { + rpc GetHealth (HealthRequest) returns (HealthResponse) { + } +} + +enum HealthState { + HEALTHY = 0; + INITIALIZING = 1; + UNHEALTHY = 2; + UNKNOWN = 3; +} + +message HealthRequest { + repeated string components = 1; +} + +message ComponentHealth { + HealthState State = 1; + string message = 2; +} + +message HealthResponse { + HealthState overallHealthState = 1; + map componentsHealth = 2; +} From 3fc1eaf0df0a27ca9f6d5015db0c2c9fef146783 Mon Sep 17 00:00:00 2001 From: Peter Kurfer Date: Mon, 15 Feb 2021 08:46:19 +0100 Subject: [PATCH 4/6] Setup project and implement first draft of audit file reader --- .editorconfig | 10 ++ .gitignore | 6 ++ INetMock.sln | 36 ++++++++ src/INetMock.Client/Audit/Details.cs | 89 ++++++++++++++++++ src/INetMock.Client/Audit/Event.cs | 91 +++++++++++++++++++ .../Audit/EventServerStreamReader.cs | 28 ++++++ src/INetMock.Client/Audit/GenericReader.cs | 45 +++++++++ src/INetMock.Client/Audit/IEventReader.cs | 22 +++++ .../Audit/INetMockHttpHeaders.cs | 18 ++++ .../Audit/IProtoEventReader.cs | 11 +++ src/INetMock.Client/Audit/ProtoReader.cs | 68 ++++++++++++++ src/INetMock.Client/Audit/TypedReader.cs | 47 ++++++++++ src/INetMock.Client/INetMock.Client.csproj | 26 ++++++ src/INetMock.Client/proto | 1 + .../Audit/GenericReaderTest.cs | 57 ++++++++++++ .../Audit/TypedReaderTest.cs | 45 +++++++++ test/INetMock.Client.Test/Hex/Converter.cs | 15 +++ .../INetMock.Client.Test.csproj | 24 +++++ 18 files changed, 639 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 INetMock.sln create mode 100644 src/INetMock.Client/Audit/Details.cs create mode 100644 src/INetMock.Client/Audit/Event.cs create mode 100644 src/INetMock.Client/Audit/EventServerStreamReader.cs create mode 100644 src/INetMock.Client/Audit/GenericReader.cs create mode 100644 src/INetMock.Client/Audit/IEventReader.cs create mode 100644 src/INetMock.Client/Audit/INetMockHttpHeaders.cs create mode 100644 src/INetMock.Client/Audit/IProtoEventReader.cs create mode 100644 src/INetMock.Client/Audit/ProtoReader.cs create mode 100644 src/INetMock.Client/Audit/TypedReader.cs create mode 100644 src/INetMock.Client/INetMock.Client.csproj create mode 120000 src/INetMock.Client/proto create mode 100644 test/INetMock.Client.Test/Audit/GenericReaderTest.cs create mode 100644 test/INetMock.Client.Test/Audit/TypedReaderTest.cs create mode 100644 test/INetMock.Client.Test/Hex/Converter.cs create mode 100644 test/INetMock.Client.Test/INetMock.Client.Test.csproj diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..13fb40e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 + +[*.cs] +indent_style = space +indent_size = 4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f82761d --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin/ +obj/ +.idea/ +.vscode/ + +INetMock.sln.DotSettings.user diff --git a/INetMock.sln b/INetMock.sln new file mode 100644 index 0000000..a548414 --- /dev/null +++ b/INetMock.sln @@ -0,0 +1,36 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "INetMock.Client", "src\INetMock.Client\INetMock.Client.csproj", "{4B1CFFB0-23B5-4238-BFCC-4155F459FF8E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{DF7A15B0-72D0-4BA1-9C9E-424343F6D530}" +ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + .gitignore = .gitignore +EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{07100561-E3C0-4B95-92E1-D2D3BA12C3A6}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{E7144CE4-3E55-431B-8835-E804C664A694}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "INetMock.Client.Test", "test\INetMock.Client.Test\INetMock.Client.Test.csproj", "{BFC06300-C82F-4A24-A10E-5C7D66208280}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4B1CFFB0-23B5-4238-BFCC-4155F459FF8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4B1CFFB0-23B5-4238-BFCC-4155F459FF8E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4B1CFFB0-23B5-4238-BFCC-4155F459FF8E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4B1CFFB0-23B5-4238-BFCC-4155F459FF8E}.Release|Any CPU.Build.0 = Release|Any CPU + {BFC06300-C82F-4A24-A10E-5C7D66208280}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BFC06300-C82F-4A24-A10E-5C7D66208280}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BFC06300-C82F-4A24-A10E-5C7D66208280}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BFC06300-C82F-4A24-A10E-5C7D66208280}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {4B1CFFB0-23B5-4238-BFCC-4155F459FF8E} = {07100561-E3C0-4B95-92E1-D2D3BA12C3A6} + {BFC06300-C82F-4A24-A10E-5C7D66208280} = {E7144CE4-3E55-431B-8835-E804C664A694} + EndGlobalSection +EndGlobal diff --git a/src/INetMock.Client/Audit/Details.cs b/src/INetMock.Client/Audit/Details.cs new file mode 100644 index 0000000..3adf1a0 --- /dev/null +++ b/src/INetMock.Client/Audit/Details.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using Google.Protobuf.WellKnownTypes; +using INetMock.Client.Audit.Details; + +namespace INetMock.Client.Audit +{ + public abstract record EventDetails; + public record EmptyDetails : EventDetails; + + public record GenericDetails : EventDetails + { + private readonly Any? _detailsAny; + public GenericDetails() + { + _detailsAny = null; + } + + public GenericDetails(Any? any) + { + _detailsAny = any; + } + + public static implicit operator HttpDetails(GenericDetails gd) + { + if (gd._detailsAny == null || gd._detailsAny.Value == null) return new(); + //if(!gd._detailsAny.TypeUrl.EndsWith(HTTPDetailsEntity.Descriptor.FullName)) throw new InvalidOperationException(); + return new HttpDetails(gd._detailsAny); + } + + public static implicit operator DnsDetails(GenericDetails gd) + { + if (gd._detailsAny == null || gd._detailsAny.Value == null) return new(); + //if(!gd._detailsAny.TypeUrl.EndsWith(HTTPDetailsEntity.Descriptor.FullName)) throw new InvalidOperationException(); + return new DnsDetails(gd._detailsAny); + } + } + + public record HttpDetails : EventDetails + { + public HttpDetails() + { + + } + + public HttpDetails(Any? any) + { + if (any == null || any.Value == null) return; + + var detailsEntity = HTTPDetailsEntity.Parser.ParseFrom(any.Value); + + Method = new HttpMethod(detailsEntity.Method.ToString()); + Host = detailsEntity.Host; + Uri = detailsEntity.Uri; + Proto = detailsEntity.Proto; + Headers = new INetMockHttpHeaders(detailsEntity.Headers); + } + + public HttpMethod Method { get; init; } + public string Host { get; init; } + public string Uri { get; init; } + public string Proto { get; init; } + public HttpHeaders Headers { get; init; } + } + + public record DnsDetails : EventDetails + { + public DnsDetails() + { + + } + + public DNSOpCode OpCode { get; init; } + + public IReadOnlyList Questions { get; init; } + + public DnsDetails(Any? any) + { + if(any == null || any.Value == null) return; + + var entity = DNSDetailsEntity.Parser.ParseFrom(any.Value); + OpCode = entity.Opcode; + Questions = entity.Questions; + } + } +} diff --git a/src/INetMock.Client/Audit/Event.cs b/src/INetMock.Client/Audit/Event.cs new file mode 100644 index 0000000..df66728 --- /dev/null +++ b/src/INetMock.Client/Audit/Event.cs @@ -0,0 +1,91 @@ +using System; +using System.Net; + +namespace INetMock.Client.Audit +{ + public record Event where T : EventDetails, new() + { + public Event() + { + + } + + public Event(EventEntity entity) + { + if (entity == null) throw new ArgumentNullException(nameof(entity)); + + var details = (new T(), entity.Application) switch + { + (GenericDetails, _) => new GenericDetails(entity.ProtocolDetails) as T, + (_, AppProtocol.Dns) => new DnsDetails(entity.ProtocolDetails) as T, + (_, AppProtocol.Http or AppProtocol.HttpProxy) => new HttpDetails(entity.ProtocolDetails) as T, + (_, _) => new EmptyDetails() as T + }; + + Id = entity.Id; + Timestamp = entity.Timestamp.ToDateTimeOffset(); + Transport = entity.Transport; + SourceIp = new IPAddress(entity.SourceIP.Span); + SourcePort = Convert.ToUInt16(entity.SourcePort); + DestinationIp = new IPAddress(entity.DestinationIP.Span); + DestinationPort = Convert.ToUInt16(entity.DestinationPort); + TlsDetails = entity.Tls; + Application = entity.Application; + Details = details; + } + + public long Id { get; init; } + public DateTimeOffset Timestamp { get; init; } + public TransportProtocol Transport { get; init; } + public AppProtocol Application { get; init; } + public IPAddress SourceIp { get; init; } + public IPAddress DestinationIp { get; init; } + public ushort SourcePort { get; init; } + public ushort DestinationPort { get; init; } + public TLSDetailsEntity TlsDetails { get; init; } + public T? Details { get; init; } + + public bool CanConvert() where TTarget : EventDetails, new() + { + if (Details == null) return false; + + return (new TTarget(), Application) switch + { + (DnsDetails, AppProtocol.Dns) => true, + (HttpDetails, AppProtocol.Http or AppProtocol.HttpProxy) => true, + (_, _) => false + }; + } + + public static explicit operator Event?(Event ge) + { + T? details = null; + if (ge.Details != null) + { + details = ge.Application switch + { + AppProtocol.Dns => (DnsDetails) ge.Details as T, + AppProtocol.Http or AppProtocol.HttpProxy => (HttpDetails) ge.Details as T, + _ => new EmptyDetails() as T + }; + } + + + if (details == null) return null; + + return new() + { + Id = ge.Id, + Timestamp = ge.Timestamp, + Transport = ge.Transport, + SourceIp = ge.SourceIp, + SourcePort = ge.SourcePort, + DestinationIp = ge.DestinationIp, + DestinationPort = ge.DestinationPort, + TlsDetails = ge.TlsDetails, + Application = ge.Application, + Details = details + }; + } + } +} diff --git a/src/INetMock.Client/Audit/EventServerStreamReader.cs b/src/INetMock.Client/Audit/EventServerStreamReader.cs new file mode 100644 index 0000000..ce754a9 --- /dev/null +++ b/src/INetMock.Client/Audit/EventServerStreamReader.cs @@ -0,0 +1,28 @@ +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; + +namespace INetMock.Client.Audit +{ + public sealed class EventServerStreamReader : IProtoEventReader + { + private readonly IAsyncStreamReader _asyncEventStream; + + public EventServerStreamReader(IAsyncStreamReader asyncEventStream) + { + _asyncEventStream = asyncEventStream; + } + + public async Task ReadAsync(CancellationToken token = default) + { + if (!await _asyncEventStream.MoveNext(token)) return null; + return _asyncEventStream.Current; + } + + public void Dispose() + { + } + + public ValueTask DisposeAsync() => ValueTask.CompletedTask; + } +} diff --git a/src/INetMock.Client/Audit/GenericReader.cs b/src/INetMock.Client/Audit/GenericReader.cs new file mode 100644 index 0000000..2d8dcbf --- /dev/null +++ b/src/INetMock.Client/Audit/GenericReader.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; + +namespace INetMock.Client.Audit +{ + public sealed class GenericReader : IEventReader + { + private readonly IProtoEventReader _reader; + + public GenericReader(IProtoEventReader reader) + { + _reader = reader; + } + + public async IAsyncEnumerable> ReadAllAsync([EnumeratorCancellation] CancellationToken token = default) + { + while (true) + { + var ev = await ReadAsync(token); + if (ev == null) + { + yield break; + } + + yield return ev; + } + } + + public async Task?> ReadAsync(CancellationToken token = default) + { + var entity = await _reader.ReadAsync(token); + if (entity == null) return null; + return new Event(entity); + } + + public ValueTask DisposeAsync() => _reader.DisposeAsync(); + + public void Dispose() + { + _reader.Dispose(); + } + } +} diff --git a/src/INetMock.Client/Audit/IEventReader.cs b/src/INetMock.Client/Audit/IEventReader.cs new file mode 100644 index 0000000..bcff725 --- /dev/null +++ b/src/INetMock.Client/Audit/IEventReader.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace INetMock.Client.Audit +{ + public interface IEventReader: IDisposable, IAsyncDisposable where T : EventDetails, new() + { + IAsyncEnumerable> ReadAllAsync(CancellationToken token = default); + + /// + /// + /// + /// + /// + /// An event as long as underlying stream has data. + /// When the end of the stream has been reached it will return null. + /// + Task?> ReadAsync(CancellationToken token = default); + } +} diff --git a/src/INetMock.Client/Audit/INetMockHttpHeaders.cs b/src/INetMock.Client/Audit/INetMockHttpHeaders.cs new file mode 100644 index 0000000..69e1fcf --- /dev/null +++ b/src/INetMock.Client/Audit/INetMockHttpHeaders.cs @@ -0,0 +1,18 @@ +using System.Net.Http.Headers; +using Google.Protobuf.Collections; +using INetMock.Client.Audit.Details; + +namespace INetMock.Client.Audit +{ + internal class INetMockHttpHeaders : HttpHeaders + { + internal INetMockHttpHeaders(MapField headers) + { + foreach (var (key, values) in headers) + { + if (string.IsNullOrEmpty(key) || values == null) continue; + Add(key, values.Values); + } + } + } +} diff --git a/src/INetMock.Client/Audit/IProtoEventReader.cs b/src/INetMock.Client/Audit/IProtoEventReader.cs new file mode 100644 index 0000000..01e434e --- /dev/null +++ b/src/INetMock.Client/Audit/IProtoEventReader.cs @@ -0,0 +1,11 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace INetMock.Client.Audit +{ + public interface IProtoEventReader : IDisposable, IAsyncDisposable + { + Task ReadAsync(CancellationToken token = default); + } +} diff --git a/src/INetMock.Client/Audit/ProtoReader.cs b/src/INetMock.Client/Audit/ProtoReader.cs new file mode 100644 index 0000000..b2534d9 --- /dev/null +++ b/src/INetMock.Client/Audit/ProtoReader.cs @@ -0,0 +1,68 @@ +using System.Buffers; +using System.Buffers.Binary; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Google.Protobuf; + +namespace INetMock.Client.Audit +{ + public sealed class ProtoReader : IProtoEventReader + { + private readonly MemoryPool _memoryPool; + private readonly Stream _sourceStream; + private readonly bool _keepStreamOpen; + + public ProtoReader(Stream sourceStream, bool keepStreamOpen = false) + { + _memoryPool = MemoryPool.Shared; + _sourceStream = sourceStream; + _keepStreamOpen = keepStreamOpen; + } + + public async Task ReadAsync(CancellationToken token = default) + { + using var rentedLengthMem = _memoryPool.Rent(4); + var lengthMem = rentedLengthMem.Memory.Slice(0, 4); + var read = await _sourceStream.ReadAsync(lengthMem, token); + if (read != 4) + { + return null; + } + + var messageLength = BinaryPrimitives.ReadInt32BigEndian(lengthMem.Span); + using var rentedMsgMem = _memoryPool.Rent(messageLength); + var msgMem = rentedMsgMem.Memory.Slice(0, messageLength); + read = await _sourceStream.ReadAsync(msgMem, token); + if (read != messageLength) + { + return null; + } + + var entity = new EventEntity(); + entity.MergeFrom(msgMem.ToArray()); + + return entity; + } + + public async ValueTask DisposeAsync() + { + if (!_keepStreamOpen) + { + await _sourceStream.DisposeAsync(); + } + + _memoryPool.Dispose(); + } + + public void Dispose() + { + if (!_keepStreamOpen) + { + _sourceStream?.Dispose(); + } + + _memoryPool?.Dispose(); + } + } +} diff --git a/src/INetMock.Client/Audit/TypedReader.cs b/src/INetMock.Client/Audit/TypedReader.cs new file mode 100644 index 0000000..50ac00e --- /dev/null +++ b/src/INetMock.Client/Audit/TypedReader.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; + +namespace INetMock.Client.Audit +{ + public sealed class TypedReader : IEventReader where T : EventDetails, new() + { + private readonly IProtoEventReader _reader; + + public TypedReader(IProtoEventReader reader) + { + _reader = reader; + } + + public async IAsyncEnumerable> ReadAllAsync([EnumeratorCancellation] CancellationToken token = default) + { + while (true) + { + var ev = await ReadAsync(token); + if (ev == null) + { + yield break; + } + + yield return ev; + } + } + + public async Task?> ReadAsync(CancellationToken token = default) + { + do + { + var entity = await _reader.ReadAsync(token); + if (entity == null) return null; + var parsed = new Event(entity); + if (parsed.Details == null) continue; + return parsed; + } while (true); + } + + public void Dispose() => _reader.Dispose(); + + public ValueTask DisposeAsync() => _reader.DisposeAsync(); + } +} diff --git a/src/INetMock.Client/INetMock.Client.csproj b/src/INetMock.Client/INetMock.Client.csproj new file mode 100644 index 0000000..b33a870 --- /dev/null +++ b/src/INetMock.Client/INetMock.Client.csproj @@ -0,0 +1,26 @@ + + + + net5.0 + enable + latest + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + diff --git a/src/INetMock.Client/proto b/src/INetMock.Client/proto new file mode 120000 index 0000000..8463861 --- /dev/null +++ b/src/INetMock.Client/proto @@ -0,0 +1 @@ +../../api/proto/ \ No newline at end of file diff --git a/test/INetMock.Client.Test/Audit/GenericReaderTest.cs b/test/INetMock.Client.Test/Audit/GenericReaderTest.cs new file mode 100644 index 0000000..3a52377 --- /dev/null +++ b/test/INetMock.Client.Test/Audit/GenericReaderTest.cs @@ -0,0 +1,57 @@ +using System.IO; +using INetMock.Client.Audit; +using INetMock.Client.Test.Hex; +using Xunit; + +namespace INetMock.Client.Test.Audit +{ + public class GenericReaderTest + { + private const string HttpEventPayload = "000000dd120b088092b8c398feffffff01180120022a047f00000132047f00000138d8fc0140504a3308041224544c535f45434448455f45434453415f574954485f4145535f3235365f4342435f5348411a096c6f63616c686f73745282010a34747970652e676f6f676c65617069732e636f6d2f696e65746d6f636b2e61756469742e4854545044657461696c73456e74697479124a12096c6f63616c686f73741a15687474703a2f2f6c6f63616c686f73742f6173646622084854545020312e312a1c0a0641636365707412120a106170706c69636174696f6e2f6a736f6e"; + private const string DnsEventPayload = "0000003b120b088092b8c398feffffff01180120012a100000000000000000000000000000000132100000000000000000000000000000000138d8fc014050"; + + private readonly byte[] _httpEventPayloadBytes; + private readonly byte[] _dnsEventPayloadBytes; + + public GenericReaderTest() + { + _httpEventPayloadBytes = HttpEventPayload.HexToByteArray(); + _dnsEventPayloadBytes = DnsEventPayload.HexToByteArray(); + } + + [Fact] + public async void TestRead_HttpEvent_Success() + { + await using var protoReader = new ProtoReader(new MemoryStream(_httpEventPayloadBytes)); + await using var reader = new GenericReader(protoReader); + await foreach (var ev in reader.ReadAllAsync()) + { + Assert.NotNull(ev); + + Assert.True(ev.CanConvert()); + Assert.False(ev.CanConvert()); + + var httpEvent = (Event?) ev; + var dnsEvent = (Event?) ev; + + Assert.NotNull(httpEvent); + Assert.Null(dnsEvent); + } + } + + [Fact] + public async void TestRead_DnsEvent_Success() + { + await using var memStream = new MemoryStream(_dnsEventPayloadBytes); + await using var protoReader = new ProtoReader(new MemoryStream(_dnsEventPayloadBytes)); + await using var reader = new GenericReader(protoReader); + await foreach (var ev in reader.ReadAllAsync()) + { + Assert.NotNull(ev); + Assert.True(ev.CanConvert()); + var dnsEvent = (Event?) ev; + Assert.NotNull(dnsEvent); + } + } + } +} diff --git a/test/INetMock.Client.Test/Audit/TypedReaderTest.cs b/test/INetMock.Client.Test/Audit/TypedReaderTest.cs new file mode 100644 index 0000000..48efa31 --- /dev/null +++ b/test/INetMock.Client.Test/Audit/TypedReaderTest.cs @@ -0,0 +1,45 @@ +using System.IO; +using INetMock.Client.Audit; +using INetMock.Client.Test.Hex; +using Xunit; + +namespace INetMock.Client.Test.Audit +{ + public class TypedReaderTest + { + private const string HttpEventPayload = "000000dd120b088092b8c398feffffff01180120022a047f00000132047f00000138d8fc0140504a3308041224544c535f45434448455f45434453415f574954485f4145535f3235365f4342435f5348411a096c6f63616c686f73745282010a34747970652e676f6f676c65617069732e636f6d2f696e65746d6f636b2e61756469742e4854545044657461696c73456e74697479124a12096c6f63616c686f73741a15687474703a2f2f6c6f63616c686f73742f6173646622084854545020312e312a1c0a0641636365707412120a106170706c69636174696f6e2f6a736f6e"; + private const string DnsEventPayload = "0000003b120b088092b8c398feffffff01180120012a100000000000000000000000000000000132100000000000000000000000000000000138d8fc014050"; + + private readonly byte[] _httpEventPayloadBytes; + private readonly byte[] _dnsEventPayloadBytes; + + public TypedReaderTest() + { + _httpEventPayloadBytes = HttpEventPayload.HexToByteArray(); + _dnsEventPayloadBytes = DnsEventPayload.HexToByteArray(); + } + + [Fact] + public async void TestRead_HttpEvent_Success() + { + await using var protoReader = new ProtoReader(new MemoryStream(_httpEventPayloadBytes)); + await using var reader = new TypedReader(protoReader); + await foreach (var ev in reader.ReadAllAsync()) + { + Assert.NotNull(ev); + } + } + + [Fact] + public async void TestRead_DnsEvent_Success() + { + await using var memStream = new MemoryStream(_dnsEventPayloadBytes); + await using var protoReader = new ProtoReader(new MemoryStream(_dnsEventPayloadBytes)); + await using var reader = new TypedReader(protoReader); + await foreach (var ev in reader.ReadAllAsync()) + { + Assert.NotNull(ev); + } + } + } +} diff --git a/test/INetMock.Client.Test/Hex/Converter.cs b/test/INetMock.Client.Test/Hex/Converter.cs new file mode 100644 index 0000000..4d1abd5 --- /dev/null +++ b/test/INetMock.Client.Test/Hex/Converter.cs @@ -0,0 +1,15 @@ +using System; +using System.Linq; + +namespace INetMock.Client.Test.Hex +{ + public static class Converter + { + public static byte[] HexToByteArray(this string hex) { + return Enumerable.Range(0, hex.Length) + .Where(x => x % 2 == 0) + .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) + .ToArray(); + } + } +} diff --git a/test/INetMock.Client.Test/INetMock.Client.Test.csproj b/test/INetMock.Client.Test/INetMock.Client.Test.csproj new file mode 100644 index 0000000..b0de0dc --- /dev/null +++ b/test/INetMock.Client.Test/INetMock.Client.Test.csproj @@ -0,0 +1,24 @@ + + + + net5.0 + + false + + latest + + enable + + + + + + + + + + + + + + From f8c59ddd48c875870fbf10dc95cd775f4c683f80 Mon Sep 17 00:00:00 2001 From: Peter Kurfer Date: Mon, 15 Feb 2021 08:50:46 +0100 Subject: [PATCH 5/6] tmp --- {api => api.bak}/.editorconfig | 0 {api => api.bak}/.gitignore | 0 {api => api.bak}/README.md | 0 {api => api.bak}/proto/audit/details/dns_details.proto | 0 {api => api.bak}/proto/audit/details/http_details.proto | 0 {api => api.bak}/proto/audit/event_entity.proto | 0 {api => api.bak}/proto/rpc/audit.proto | 0 {api => api.bak}/proto/rpc/health.proto | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename {api => api.bak}/.editorconfig (100%) rename {api => api.bak}/.gitignore (100%) rename {api => api.bak}/README.md (100%) rename {api => api.bak}/proto/audit/details/dns_details.proto (100%) rename {api => api.bak}/proto/audit/details/http_details.proto (100%) rename {api => api.bak}/proto/audit/event_entity.proto (100%) rename {api => api.bak}/proto/rpc/audit.proto (100%) rename {api => api.bak}/proto/rpc/health.proto (100%) diff --git a/api/.editorconfig b/api.bak/.editorconfig similarity index 100% rename from api/.editorconfig rename to api.bak/.editorconfig diff --git a/api/.gitignore b/api.bak/.gitignore similarity index 100% rename from api/.gitignore rename to api.bak/.gitignore diff --git a/api/README.md b/api.bak/README.md similarity index 100% rename from api/README.md rename to api.bak/README.md diff --git a/api/proto/audit/details/dns_details.proto b/api.bak/proto/audit/details/dns_details.proto similarity index 100% rename from api/proto/audit/details/dns_details.proto rename to api.bak/proto/audit/details/dns_details.proto diff --git a/api/proto/audit/details/http_details.proto b/api.bak/proto/audit/details/http_details.proto similarity index 100% rename from api/proto/audit/details/http_details.proto rename to api.bak/proto/audit/details/http_details.proto diff --git a/api/proto/audit/event_entity.proto b/api.bak/proto/audit/event_entity.proto similarity index 100% rename from api/proto/audit/event_entity.proto rename to api.bak/proto/audit/event_entity.proto diff --git a/api/proto/rpc/audit.proto b/api.bak/proto/rpc/audit.proto similarity index 100% rename from api/proto/rpc/audit.proto rename to api.bak/proto/rpc/audit.proto diff --git a/api/proto/rpc/health.proto b/api.bak/proto/rpc/health.proto similarity index 100% rename from api/proto/rpc/health.proto rename to api.bak/proto/rpc/health.proto From 08b8eb82f54325faf2f86806c6ab4f40e9fc0af1 Mon Sep 17 00:00:00 2001 From: Peter Kurfer Date: Mon, 15 Feb 2021 08:51:54 +0100 Subject: [PATCH 6/6] Merge subtree --- proto/audit/details/dns_details.proto | 1 + proto/audit/details/http_details.proto | 3 ++- proto/audit/event_entity.proto | 3 ++- proto/rpc/audit.proto | 5 +++-- proto/rpc/health.proto | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/proto/audit/details/dns_details.proto b/proto/audit/details/dns_details.proto index 3dc2fb8..f820840 100644 --- a/proto/audit/details/dns_details.proto +++ b/proto/audit/details/dns_details.proto @@ -4,6 +4,7 @@ option go_package = "gitlab.com/inetmock/inetmock/pkg/audit/details"; option java_multiple_files = true; option java_package = "com.github.baez90.inetmock.audit.details"; option java_outer_classname = "HandlerEventProto"; +option csharp_namespace = "INetMock.Client.Audit.Details"; package inetmock.audit.details; diff --git a/proto/audit/details/http_details.proto b/proto/audit/details/http_details.proto index e9a3955..e5e5d7a 100644 --- a/proto/audit/details/http_details.proto +++ b/proto/audit/details/http_details.proto @@ -4,6 +4,7 @@ option go_package = "gitlab.com/inetmock/inetmock/pkg/audit/details"; option java_multiple_files = true; option java_package = "com.github.baez90.inetmock.audit.details"; option java_outer_classname = "HandlerEventProto"; +option csharp_namespace = "INetMock.Client.Audit.Details"; package inetmock.audit.details; @@ -29,4 +30,4 @@ message HTTPDetailsEntity { string uri = 3; string proto = 4; map headers = 5; -} \ No newline at end of file +} diff --git a/proto/audit/event_entity.proto b/proto/audit/event_entity.proto index 00492af..3b213ee 100644 --- a/proto/audit/event_entity.proto +++ b/proto/audit/event_entity.proto @@ -4,6 +4,7 @@ option go_package = "gitlab.com/inetmock/inetmock/pkg/audit"; option java_multiple_files = true; option java_package = "com.github.baez90.inetmock.audit"; option java_outer_classname = "HandlerEventProto"; +option csharp_namespace = "INetMock.Client.Audit"; package inetmock.audit; @@ -48,4 +49,4 @@ message EventEntity { uint32 destinationPort = 8; TLSDetailsEntity tls = 9; google.protobuf.Any protocolDetails = 10; -} \ No newline at end of file +} diff --git a/proto/rpc/audit.proto b/proto/rpc/audit.proto index 1d43bd3..ca369c1 100644 --- a/proto/rpc/audit.proto +++ b/proto/rpc/audit.proto @@ -4,8 +4,9 @@ option go_package = "gitlab.com/inetmock/inetmock/pkg/rpc"; option java_multiple_files = true; option java_package = "com.github.baez90.inetmock.rpc"; option java_outer_classname = "AuditProto"; +option csharp_namespace = "INetMock.Client.Rpc"; -import 'pkg/audit/event_entity.proto'; +import 'proto/audit/event_entity.proto'; package inetmock.rpc; @@ -49,4 +50,4 @@ service Audit { rpc RegisterFileSink (RegisterFileSinkRequest) returns (RegisterFileSinkResponse); rpc RemoveFileSink (RemoveFileSinkRequest) returns (RemoveFileSinkResponse); rpc ListSinks(ListSinksRequest) returns (ListSinksResponse); -} \ No newline at end of file +} diff --git a/proto/rpc/health.proto b/proto/rpc/health.proto index 3ff1aee..193bc8c 100644 --- a/proto/rpc/health.proto +++ b/proto/rpc/health.proto @@ -4,6 +4,7 @@ option go_package = "gitlab.com/inetmock/inetmock/internal/rpc"; option java_multiple_files = true; option java_package = "com.github.baez90.inetmock.rpc"; option java_outer_classname = "HealthProto"; +option csharp_namespace = "INetMock.Client.Rpc"; package inetmock.rpc; @@ -31,4 +32,4 @@ message ComponentHealth { message HealthResponse { HealthState overallHealthState = 1; map componentsHealth = 2; -} \ No newline at end of file +}