-
-
Notifications
You must be signed in to change notification settings - Fork 323
/
UdpDataProvider.cs
196 lines (180 loc) · 7.35 KB
/
UdpDataProvider.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
using Audit.Core;
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Audit.Udp.Providers
{
/// <summary>
/// Send Audit Logs as UDP datagrams in a network
/// </summary>
/// <remarks>
/// Settings:
/// - RemoteAddress: remote host or multicast group to send the events.
/// - RemotePort: remote port to send the events.
/// - MulticastMode: to indicate if the RemoteAddress is a multicast group.
/// - CustomSerializer: to specify a custom serialization method to send UDP packets.
/// - CustomDeserializer: to specify a custom deserialization method to receive UDP packets.
/// </remarks>
public class UdpDataProvider : AuditDataProvider
{
/// <summary>
/// Gets or sets the address of the remote host or multicast group to which the underlying UdpClient should send the audit events.
/// </summary>
public IPAddress RemoteAddress { get; set; }
/// <summary>
/// Gets or sets the port number of the remote host or multicast group to which the underlying UdpClient should send the audit events.
/// </summary>
public int RemotePort { get; set; }
/// <summary>
/// Gets or sets the multicast mode.
/// Auto: (default) Multicast is automatically detected from the IP address.
/// Enabled: Multicast explicitly enabled.
/// Disabled: Multicast explicitly disabled.
/// </summary>
public MulticastMode MulticastMode { get; set; }
/// <summary>
/// Gets or sets a custom serialization method to send UDP packets.
/// </summary>
public Func<AuditEvent, byte[]> CustomSerializer { get; set; }
/// <summary>
/// Gets or sets a custom deserialization method to receive UDP packets.
/// </summary>
public Func<byte[], AuditEvent> CustomDeserializer { get; set; }
private UdpClient _clientSend;
private object _lockerSend = new object();
private UdpClient _clientReceive;
private object _lockReceive = new object();
/// <summary>
/// Sends an event to the network as an UDP datagram
/// </summary>
/// <param name="auditEvent">The audit event being created.</param>
public override object InsertEvent(AuditEvent auditEvent)
{
var eventId = Guid.NewGuid();
Send(eventId, auditEvent);
return eventId;
}
/// <summary>
/// Sends an event to the network asynchronously as an UDP datagram
/// </summary>
/// <param name="auditEvent">The audit event being created.</param>
/// <param name="cancellationToken">The Cancellation Token.</param>
public override async Task<object> InsertEventAsync(AuditEvent auditEvent, CancellationToken cancellationToken = default)
{
var eventId = Guid.NewGuid();
await SendAsync(eventId, auditEvent);
return eventId;
}
/// <summary>
/// Sends an event to the network as an UDP datagram, related to a previous event
/// </summary>
/// <param name="auditEvent">The audit event.</param>
/// <param name="eventId">The event id being replaced.</param>
public override void ReplaceEvent(object eventId, AuditEvent auditEvent)
{
Send(eventId, auditEvent);
}
/// <summary>
/// Sends an event to the network asynchronously as an UDP datagram, related to a previous event
/// </summary>
/// <param name="auditEvent">The audit event.</param>
/// <param name="eventId">The event id being replaced.</param>
/// <param name="cancellationToken">The Cancellation Token.</param>
public override async Task ReplaceEventAsync(object eventId, AuditEvent auditEvent, CancellationToken cancellationToken = default)
{
await SendAsync(eventId, auditEvent);
}
/// <summary>
/// Receives an event from the network as an UDP datagram.
/// The returned Task object will complete after the UDP packet has been received.
/// </summary>
public async Task<AuditEvent> ReceiveAsync()
{
var client = GetReceiverClient();
var result = await client.ReceiveAsync();
return DeserializeEvent(result.Buffer);
}
private void Send(object eventId, AuditEvent auditEvent)
{
var client = GetSendClient();
var ep = new IPEndPoint(RemoteAddress, RemotePort);
auditEvent.CustomFields["UdpEventId"] = eventId;
var buffer = SerializeEvent(auditEvent);
client.SendAsync(buffer, buffer.Length, ep).GetAwaiter().GetResult();
}
private async Task SendAsync(object eventId, AuditEvent auditEvent)
{
var client = GetSendClient();
var ep = new IPEndPoint(RemoteAddress, RemotePort);
auditEvent.CustomFields["UdpEventId"] = eventId;
var buffer = SerializeEvent(auditEvent);
await client.SendAsync(buffer, buffer.Length, ep);
}
private byte[] SerializeEvent(AuditEvent auditEvent)
{
if (CustomSerializer != null)
{
return CustomSerializer.Invoke(auditEvent);
}
return Encoding.UTF8.GetBytes(auditEvent.ToJson());
}
private AuditEvent DeserializeEvent(byte[] data)
{
if (CustomDeserializer != null)
{
return CustomDeserializer.Invoke(data);
}
return AuditEvent.FromJson(Encoding.UTF8.GetString(data));
}
private UdpClient GetSendClient()
{
lock (_lockerSend)
{
if (_clientSend == null)
{
_clientSend = new UdpClient();
if (IsMulticast())
{
_clientSend.JoinMulticastGroup(RemoteAddress);
}
}
}
return _clientSend;
}
private UdpClient GetReceiverClient()
{
lock (_lockReceive)
{
if (_clientReceive == null)
{
_clientReceive = new UdpClient();
_clientReceive.ExclusiveAddressUse = false;
var ep = new IPEndPoint(IPAddress.Any, RemotePort);
_clientReceive.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
_clientReceive.Client.Bind(ep);
if (IsMulticast())
{
_clientReceive.JoinMulticastGroup(RemoteAddress);
}
}
}
return _clientReceive;
}
private bool IsMulticast()
{
if (MulticastMode == MulticastMode.Auto)
{
// 224.0.0.0 to 239.255.255.255 are multicast (https://en.wikipedia.org/wiki/Multicast_address)
byte firstByte = RemoteAddress.GetAddressBytes()[0];
return firstByte >= 224 && firstByte < 240;
}
else
{
return MulticastMode == MulticastMode.Enabled;
}
}
}
}