Wake on LAN support
parent
3d635dab60
commit
79c43df49e
|
@ -0,0 +1,8 @@
|
||||||
|
namespace NetworkResurrector.Abstractions
|
||||||
|
{
|
||||||
|
public interface INotifier
|
||||||
|
{
|
||||||
|
void NotifyMessage(string message);
|
||||||
|
void NotifyError(string message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace NetworkResurrector.Abstractions
|
||||||
|
{
|
||||||
|
public interface IWakeOnLanService
|
||||||
|
{
|
||||||
|
Task<(bool success, string message)> Wake(string macAddress);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -1,6 +1,7 @@
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using NetworkResurrector.Application.Commands;
|
||||||
using NetworkResurrector.Application.Queries;
|
using NetworkResurrector.Application.Queries;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
@ -31,5 +32,12 @@ namespace NetworkResurrector.Api.Controllers
|
||||||
{
|
{
|
||||||
return Ok("Valid");
|
return Ok("Valid");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("wake")]
|
||||||
|
public async Task<IActionResult> WakeMachine([FromBody] WakeMachine wakeMachine)
|
||||||
|
{
|
||||||
|
var result = await _mediator.Send(wakeMachine);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using NetworkResurrector.WakeOnLan;
|
||||||
|
|
||||||
|
namespace NetworkResurrector.Api.Extensions
|
||||||
|
{
|
||||||
|
public static class WakeOnLanExtensions
|
||||||
|
{
|
||||||
|
public static void AddWakeOnLan(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.AddWakeOnLanService();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\NetworkResurrector.Application\NetworkResurrector.Application.csproj" />
|
<ProjectReference Include="..\NetworkResurrector.Application\NetworkResurrector.Application.csproj" />
|
||||||
|
<ProjectReference Include="..\NetworkResurrector.WakeOnLan\NetworkResurrector.WakeOnLan.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -8,6 +8,7 @@ using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using NetworkResurrector.Api.Authentication;
|
using NetworkResurrector.Api.Authentication;
|
||||||
|
using NetworkResurrector.Api.Extensions;
|
||||||
using NetworkResurrector.Api.Swagger;
|
using NetworkResurrector.Api.Swagger;
|
||||||
using NetworkResurrector.Application;
|
using NetworkResurrector.Application;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
@ -17,13 +18,13 @@ namespace NetworkResurrector.Api
|
||||||
{
|
{
|
||||||
public class Startup
|
public class Startup
|
||||||
{
|
{
|
||||||
|
private readonly IConfiguration _configuration;
|
||||||
|
|
||||||
public Startup(IConfiguration configuration)
|
public Startup(IConfiguration configuration)
|
||||||
{
|
{
|
||||||
Configuration = configuration;
|
_configuration = configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IConfiguration Configuration { get; }
|
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to add services to the container.
|
// This method gets called by the runtime. Use this method to add services to the container.
|
||||||
public void ConfigureServices(IServiceCollection services)
|
public void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
|
@ -48,6 +49,9 @@ namespace NetworkResurrector.Api
|
||||||
|
|
||||||
// Application
|
// Application
|
||||||
services.AddApplicationServices();
|
services.AddApplicationServices();
|
||||||
|
|
||||||
|
// WakeOnLan
|
||||||
|
services.AddWakeOnLan(_configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using NetworkResurrector.Abstractions;
|
||||||
|
using NetworkResurrector.Application.Commands;
|
||||||
|
using NetworkResurrector.Application.Events;
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace NetworkResurrector.Application.CommandHandlers
|
||||||
|
{
|
||||||
|
public class WakeMachineHandler : IRequestHandler<WakeMachine, MachineWaked>
|
||||||
|
{
|
||||||
|
private readonly ILogger<WakeMachineHandler> _logger;
|
||||||
|
private readonly IWakeOnLanService _wakeOnLanService;
|
||||||
|
|
||||||
|
public WakeMachineHandler(ILogger<WakeMachineHandler> logger, IWakeOnLanService wakeOnLanService)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_wakeOnLanService = wakeOnLanService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<MachineWaked> Handle(WakeMachine command, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var (success, status) = await _wakeOnLanService.Wake(command.MacAddress);
|
||||||
|
return new MachineWaked(success, status);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
var correlationIdMsg = $"CorrelationId: {command.Metadata.CorrelationId}";
|
||||||
|
_logger.LogError(ex, $"An unexpected error has occurred. {correlationIdMsg}");
|
||||||
|
return new MachineWaked(false, $"{ex.Message} {correlationIdMsg}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
using NetworkResurrector.Application.Events;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NetworkResurrector.Application.Commands
|
||||||
|
{
|
||||||
|
public class WakeMachine : Command<MachineWaked>
|
||||||
|
{
|
||||||
|
public string MacAddress { get; set; }
|
||||||
|
|
||||||
|
public WakeMachine(string macAddress) : base(new Metadata() { CorrelationId = Guid.NewGuid() })
|
||||||
|
{
|
||||||
|
MacAddress = macAddress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
namespace NetworkResurrector.Application.Events
|
||||||
|
{
|
||||||
|
public class MachineWaked
|
||||||
|
{
|
||||||
|
public bool Success { get; }
|
||||||
|
public string Status { get; }
|
||||||
|
|
||||||
|
public MachineWaked(bool success, string status)
|
||||||
|
{
|
||||||
|
Success = success;
|
||||||
|
Status = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\NetworkResurrector.Abstractions\NetworkResurrector.Abstractions.csproj" />
|
||||||
<ProjectReference Include="..\NetworkResurrector.Domain\NetworkResurrector.Domain.csproj" />
|
<ProjectReference Include="..\NetworkResurrector.Domain\NetworkResurrector.Domain.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using NetworkResurrector.Abstractions;
|
||||||
|
|
||||||
|
namespace NetworkResurrector.WakeOnLan
|
||||||
|
{
|
||||||
|
public static class DependencyInjectionExtensions
|
||||||
|
{
|
||||||
|
public static void AddWakeOnLanService(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddScoped<WolClient>();
|
||||||
|
services.AddScoped<IWakeOnLanService, WolDriver>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsPackageVersion)" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsPackageVersion)" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\NetworkResurrector.Abstractions\NetworkResurrector.Abstractions.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -0,0 +1,45 @@
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
|
||||||
|
namespace NetworkResurrector.WakeOnLan
|
||||||
|
{
|
||||||
|
internal class WolClient : UdpClient
|
||||||
|
{
|
||||||
|
private readonly ILogger<WolClient> _logger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of <see cref="WolClient"/>.
|
||||||
|
/// </summary>
|
||||||
|
public WolClient(ILogger<WolClient> logger) : base()
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets up the UDP client to broadcast packets.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><see langword="true"/> if the UDP client is set in
|
||||||
|
/// broadcast mode.</returns>
|
||||||
|
public bool SetClientInBrodcastMode()
|
||||||
|
{
|
||||||
|
bool broadcast = false;
|
||||||
|
if (this.Active)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 0);
|
||||||
|
_logger.LogInformation("WolClient => SetClientInBrodcastMode succedded.");
|
||||||
|
broadcast = true;
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
broadcast = false;
|
||||||
|
_logger.LogError("WolClient => SetClientInBrodcastMode failed.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return broadcast;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
using NetworkResurrector.Abstractions;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace NetworkResurrector.WakeOnLan
|
||||||
|
{
|
||||||
|
internal class WolDriver : IWakeOnLanService
|
||||||
|
{
|
||||||
|
private readonly WolClient _client;
|
||||||
|
|
||||||
|
public WolDriver(WolClient client)
|
||||||
|
{
|
||||||
|
_client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<(bool success, string message)> Wake(string macAddress)
|
||||||
|
{
|
||||||
|
//remove all non 0-9, A-F, a-f characters
|
||||||
|
macAddress = Regex.Replace(macAddress, @"[^0-9A-Fa-f]", "");
|
||||||
|
|
||||||
|
//check if mac adress length is valid
|
||||||
|
if (macAddress.Length != 12)
|
||||||
|
return (false, "Invalid MAC address.");
|
||||||
|
else
|
||||||
|
return await Wakeup(macAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Wakeup
|
||||||
|
/// <summary>
|
||||||
|
/// Wakes up the machine with the given <paramref name="macAddress"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>
|
||||||
|
/// <note>
|
||||||
|
/// <list type="number">
|
||||||
|
/// <item> The motherboard must support Wake On LAN.</item>
|
||||||
|
/// <item> The NIC must support Wake On LAN.</item>
|
||||||
|
/// <item> There must be a wire connecting the motherboard's WOL port to
|
||||||
|
/// the NIC's WOL port. Usually there always is a connection on most of
|
||||||
|
/// the PCs.</item>
|
||||||
|
/// <item> The Wake On LAN feature must be enabled in the motherboard's
|
||||||
|
/// BIOS. Usually this is also enabled by default, but you might like to
|
||||||
|
/// check again.</item>
|
||||||
|
/// <item> The "Good Connection" light on the back of the NIC must be lit
|
||||||
|
/// when the machine is off. (By default always good if you are not
|
||||||
|
/// facing any network issues)</item>
|
||||||
|
/// <item> Port 12287 (0x2FFF) must be open. (By default it should be
|
||||||
|
/// open unless some antivirus or any other such program has changed
|
||||||
|
/// settings.)</item>
|
||||||
|
/// <item> Packets cannot be broadcast across the Internet. That's why
|
||||||
|
/// it's called Wake On Lan, not Wake On Internet.</item>
|
||||||
|
/// <item> To find your MAC address, run the MSINFO32.EXE tool that is a
|
||||||
|
/// part of Windows and navigate to Components > Network > Adapter
|
||||||
|
/// or simply type nbtstat -a <your hostname < at command prompt.
|
||||||
|
/// e.g. nbtstat -a mymachinename or nbtstat -A 10.2.100.213.
|
||||||
|
/// <param name="macAddress">The MAC address of the host which has to be
|
||||||
|
/// woken up.</param>
|
||||||
|
///
|
||||||
|
private async Task<(bool success, string message)> Wakeup(string macAddress)
|
||||||
|
{
|
||||||
|
_client.Connect(new IPAddress(0xffffffff) /*255.255.255.255 i.e broadcast*/, 0x2fff /*port = 12287*/);
|
||||||
|
|
||||||
|
if (!_client.SetClientInBrodcastMode())
|
||||||
|
return (false, "Remote client could not be set in broadcast mode!");
|
||||||
|
|
||||||
|
int byteCount = 0;
|
||||||
|
byte[] bytes = new byte[102];
|
||||||
|
|
||||||
|
for (int trailer = 0; trailer < 6; trailer++)
|
||||||
|
{
|
||||||
|
bytes[byteCount++] = 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int macPackets = 0; macPackets < 16; macPackets++)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
for (int macBytes = 0; macBytes < 6; macBytes++)
|
||||||
|
{
|
||||||
|
bytes[byteCount++] = byte.Parse(macAddress.Substring(i, 2), NumberStyles.HexNumber);
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int returnValue = await _client.SendAsync(bytes, byteCount);
|
||||||
|
|
||||||
|
return (true, $"{returnValue} bytes sent to {macAddress}");
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,9 +13,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||||
Directory.Build.props = Directory.Build.props
|
Directory.Build.props = Directory.Build.props
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetworkResurrector.Application", "NetworkResurrector.Application\NetworkResurrector.Application.csproj", "{15D65D65-CC96-45DE-8590-AF9132889D98}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetworkResurrector.Application", "NetworkResurrector.Application\NetworkResurrector.Application.csproj", "{15D65D65-CC96-45DE-8590-AF9132889D98}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetworkResurrector.Domain", "NetworkResurrector.Domain\NetworkResurrector.Domain.csproj", "{EC78E88E-22DC-4FFD-881E-DEECF0D2494E}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetworkResurrector.Domain", "NetworkResurrector.Domain\NetworkResurrector.Domain.csproj", "{EC78E88E-22DC-4FFD-881E-DEECF0D2494E}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetworkResurrector.WakeOnLan", "NetworkResurrector.WakeOnLan\NetworkResurrector.WakeOnLan.csproj", "{A7B3FCE1-70F5-4EAE-A8FB-EF938AE1D105}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetworkResurrector.Abstractions", "NetworkResurrector.Abstractions\NetworkResurrector.Abstractions.csproj", "{B7408385-ED73-4ED3-9654-9AFF8CDFDA8D}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
@ -35,6 +39,14 @@ Global
|
||||||
{EC78E88E-22DC-4FFD-881E-DEECF0D2494E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{EC78E88E-22DC-4FFD-881E-DEECF0D2494E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{EC78E88E-22DC-4FFD-881E-DEECF0D2494E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{EC78E88E-22DC-4FFD-881E-DEECF0D2494E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{EC78E88E-22DC-4FFD-881E-DEECF0D2494E}.Release|Any CPU.Build.0 = Release|Any CPU
|
{EC78E88E-22DC-4FFD-881E-DEECF0D2494E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{A7B3FCE1-70F5-4EAE-A8FB-EF938AE1D105}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{A7B3FCE1-70F5-4EAE-A8FB-EF938AE1D105}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{A7B3FCE1-70F5-4EAE-A8FB-EF938AE1D105}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{A7B3FCE1-70F5-4EAE-A8FB-EF938AE1D105}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{B7408385-ED73-4ED3-9654-9AFF8CDFDA8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B7408385-ED73-4ED3-9654-9AFF8CDFDA8D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B7408385-ED73-4ED3-9654-9AFF8CDFDA8D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B7408385-ED73-4ED3-9654-9AFF8CDFDA8D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
@ -9,7 +9,5 @@
|
||||||
<AutoMapperExtensionsPackageVersion>7.0.0</AutoMapperExtensionsPackageVersion>
|
<AutoMapperExtensionsPackageVersion>7.0.0</AutoMapperExtensionsPackageVersion>
|
||||||
<MediatRPackageVersion>6.0.0</MediatRPackageVersion>
|
<MediatRPackageVersion>6.0.0</MediatRPackageVersion>
|
||||||
<SwashbucklePackageVersion>5.3.1</SwashbucklePackageVersion>
|
<SwashbucklePackageVersion>5.3.1</SwashbucklePackageVersion>
|
||||||
<EntityFrameworkCorePackageVersion>3.1.3</EntityFrameworkCorePackageVersion>
|
|
||||||
<MicrosoftExtensionsPackageVersion>3.1.3</MicrosoftExtensionsPackageVersion>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
Loading…
Reference in New Issue