Radarr/NzbDrone.Core/Providers/Core/UdpProvider.cs

193 lines
5.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using NLog;
namespace NzbDrone.Core.Providers.Core
{
public class UdpProvider
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public UdpProvider()
{
}
private const int StandardPort = 9777;
private const int MaxPacketSize = 1024;
private const int HeaderSize = 32;
private const int MaxPayloadSize = MaxPacketSize - HeaderSize;
private const byte MajorVersion = 2;
private const byte MinorVersion = 0;
public enum PacketType
{
Helo = 0x01,
Bye = 0x02,
Button = 0x03,
Mouse = 0x04,
Ping = 0x05,
Broadcast = 0x06, //Currently not implemented
Notification = 0x07,
Blob = 0x08,
Log = 0x09,
Action = 0x0A,
Debug = 0xFF //Currently not implemented
}
private byte[] Header(PacketType packetType, int numberOfPackets, int currentPacket, int payloadSize, uint uniqueToken)
{
byte[] header = new byte[HeaderSize];
header[0] = (byte)'X';
header[1] = (byte)'B';
header[2] = (byte)'M';
header[3] = (byte)'C';
header[4] = MajorVersion;
header[5] = MinorVersion;
if (currentPacket == 1)
{
header[6] = (byte)(((ushort)packetType & 0xff00) >> 8);
header[7] = (byte)((ushort)packetType & 0x00ff);
}
else
{
header[6] = (byte)(((ushort)PacketType.Blob & 0xff00) >> 8);
header[7] = (byte)((ushort)PacketType.Blob & 0x00ff);
}
header[8] = (byte)((currentPacket & 0xff000000) >> 24);
header[9] = (byte)((currentPacket & 0x00ff0000) >> 16);
header[10] = (byte)((currentPacket & 0x0000ff00) >> 8);
header[11] = (byte)(currentPacket & 0x000000ff);
header[12] = (byte)((numberOfPackets & 0xff000000) >> 24);
header[13] = (byte)((numberOfPackets & 0x00ff0000) >> 16);
header[14] = (byte)((numberOfPackets & 0x0000ff00) >> 8);
header[15] = (byte)(numberOfPackets & 0x000000ff);
header[16] = (byte)((payloadSize & 0xff00) >> 8);
header[17] = (byte)(payloadSize & 0x00ff);
header[18] = (byte)((uniqueToken & 0xff000000) >> 24);
header[19] = (byte)((uniqueToken & 0x00ff0000) >> 16);
header[20] = (byte)((uniqueToken & 0x0000ff00) >> 8);
header[21] = (byte)(uniqueToken & 0x000000ff);
return header;
}
public virtual bool Send(string address, PacketType packetType, byte[] payload)
{
var uniqueToken = (uint)DateTime.Now.TimeOfDay.Milliseconds;
var socket = Connect(address, StandardPort);
if (socket == null || !socket.Connected)
{
return false;
}
try
{
bool successfull = true;
int packetCount = (payload.Length / MaxPayloadSize) + 1;
int bytesToSend = 0;
int bytesSent = 0;
int bytesLeft = payload.Length;
for (int Package = 1; Package <= packetCount; Package++)
{
if (bytesLeft > MaxPayloadSize)
{
bytesToSend = MaxPayloadSize;
bytesLeft -= bytesToSend;
}
else
{
bytesToSend = bytesLeft;
bytesLeft = 0;
}
byte[] header = Header(packetType, packetCount, Package, bytesToSend, uniqueToken);
byte[] packet = new byte[MaxPacketSize];
Array.Copy(header, 0, packet, 0, header.Length);
Array.Copy(payload, bytesSent, packet, header.Length, bytesToSend);
int sendSize = socket.Send(packet, header.Length + bytesToSend, SocketFlags.None);
if (sendSize != (header.Length + bytesToSend))
{
successfull = false;
break;
}
bytesSent += bytesToSend;
}
Disconnect(socket);
return successfull;
}
catch
{
Disconnect(socket);
return false;
}
}
private Socket Connect(string address, int port)
{
try
{
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPAddress ip;
if (!IPAddress.TryParse(address, out ip))
{
IPHostEntry ipHostEntry = Dns.GetHostEntry(address);
foreach (IPAddress ipAddress in ipHostEntry.AddressList)
{
if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
{
ip = ipAddress;
break;
}
}
}
socket.Connect(new IPEndPoint(ip, port));
return socket;
}
catch (Exception exc)
{
Logger.TraceException(exc.Message, exc);
return null;
}
}
private void Disconnect(Socket socket)
{
try
{
if (socket != null)
{
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
}
catch
{
}
}
}
}