feat: local server 添加连接
This commit is contained in:
parent
bff3e114fc
commit
02c162ee8f
8 changed files with 479 additions and 6 deletions
128
Server/Common/AES.cs
Normal file
128
Server/Common/AES.cs
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Common;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 与目标服务器通信将会加密
|
||||||
|
/// </summary>
|
||||||
|
public class AESHelper
|
||||||
|
{
|
||||||
|
static readonly byte[] Key =
|
||||||
|
[
|
||||||
|
0x11,
|
||||||
|
0xF2,
|
||||||
|
0xAF,
|
||||||
|
0xCF,
|
||||||
|
0xFF,
|
||||||
|
0x8B,
|
||||||
|
0x4C,
|
||||||
|
0x7D,
|
||||||
|
0x23,
|
||||||
|
0x96,
|
||||||
|
0x1B,
|
||||||
|
0x32,
|
||||||
|
0x43,
|
||||||
|
0xA4,
|
||||||
|
0x55,
|
||||||
|
0xF6,
|
||||||
|
0x29,
|
||||||
|
0x1C,
|
||||||
|
0x1B,
|
||||||
|
0x92,
|
||||||
|
0x23,
|
||||||
|
0x44,
|
||||||
|
0xB5,
|
||||||
|
0xF6,
|
||||||
|
];
|
||||||
|
static readonly byte[] IV =
|
||||||
|
[
|
||||||
|
0xD1,
|
||||||
|
0xF7,
|
||||||
|
0xAB,
|
||||||
|
0xCA,
|
||||||
|
0xBC,
|
||||||
|
0x7B,
|
||||||
|
0x2C,
|
||||||
|
0x3D,
|
||||||
|
0xFA,
|
||||||
|
0xAA,
|
||||||
|
0xFC,
|
||||||
|
0xA8,
|
||||||
|
0x28,
|
||||||
|
0x19,
|
||||||
|
0x9C,
|
||||||
|
0xB6,
|
||||||
|
];
|
||||||
|
|
||||||
|
public static byte[] EncryptStringToBytes_Aes(string plainText)
|
||||||
|
{
|
||||||
|
// Check arguments.
|
||||||
|
if (plainText == null || plainText.Length <= 0)
|
||||||
|
throw new ArgumentNullException(nameof(plainText), "can't be null");
|
||||||
|
byte[] encrypted;
|
||||||
|
|
||||||
|
// Create an Aes object
|
||||||
|
// with the specified key and IV.
|
||||||
|
using (Aes aesAlg = Aes.Create())
|
||||||
|
{
|
||||||
|
aesAlg.Key = Key;
|
||||||
|
aesAlg.IV = IV;
|
||||||
|
|
||||||
|
// Create an encryptor to perform the stream transform.
|
||||||
|
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
|
||||||
|
|
||||||
|
// Create the streams used for encryption.
|
||||||
|
using MemoryStream msEncrypt = new();
|
||||||
|
using CryptoStream csEncrypt = new(
|
||||||
|
msEncrypt,
|
||||||
|
encryptor,
|
||||||
|
CryptoStreamMode.Write
|
||||||
|
);
|
||||||
|
using (StreamWriter swEncrypt = new(csEncrypt))
|
||||||
|
{
|
||||||
|
//Write all data to the stream.
|
||||||
|
swEncrypt.Write(plainText);
|
||||||
|
}
|
||||||
|
encrypted = msEncrypt.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the encrypted bytes from the memory stream.
|
||||||
|
return encrypted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string DecryptStringFromBytes_Aes(byte[] cipherText)
|
||||||
|
{
|
||||||
|
// Check arguments.
|
||||||
|
if (cipherText == null || cipherText.Length <= 0)
|
||||||
|
throw new ArgumentNullException(nameof(cipherText), "can't be null");
|
||||||
|
|
||||||
|
// Declare the string used to hold
|
||||||
|
// the decrypted text.
|
||||||
|
string plaintext = string.Empty;
|
||||||
|
|
||||||
|
// Create an Aes object
|
||||||
|
// with the specified key and IV.
|
||||||
|
using (Aes aesAlg = Aes.Create())
|
||||||
|
{
|
||||||
|
aesAlg.Key = Key;
|
||||||
|
aesAlg.IV = IV;
|
||||||
|
|
||||||
|
// Create a decryptor to perform the stream transform.
|
||||||
|
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
|
||||||
|
|
||||||
|
// Create the streams used for decryption.
|
||||||
|
using MemoryStream msDecrypt = new(cipherText);
|
||||||
|
using CryptoStream csDecrypt = new(msDecrypt, decryptor, CryptoStreamMode.Read);
|
||||||
|
using StreamReader srDecrypt = new(csDecrypt);
|
||||||
|
// Read the decrypted bytes from the decrypting stream
|
||||||
|
// and place them in a string.
|
||||||
|
plaintext = srDecrypt.ReadToEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
return plaintext;
|
||||||
|
}
|
||||||
|
}
|
7
Server/Common/Message.cs
Normal file
7
Server/Common/Message.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Common;
|
||||||
|
|
||||||
|
public class SyncMsg(bool isSuccess, string body)
|
||||||
|
{
|
||||||
|
public bool IsSuccess { get; set; } = isSuccess;
|
||||||
|
public string Body { get; set; } = body;
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
@ -28,6 +29,19 @@ namespace LocalServer.Controllers
|
||||||
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
|
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Route("/macaddr")]
|
||||||
|
public string GetMacAddress()
|
||||||
|
{
|
||||||
|
NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
|
||||||
|
string macaddrs = "";
|
||||||
|
foreach (NetworkInterface nic in nics)
|
||||||
|
{
|
||||||
|
PhysicalAddress physicalAddress = nic.GetPhysicalAddress();
|
||||||
|
macaddrs += physicalAddress.ToString() + ";";
|
||||||
|
}
|
||||||
|
return macaddrs;
|
||||||
|
}
|
||||||
//TODO 是否在本地记载同步日志?
|
//TODO 是否在本地记载同步日志?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,4 +10,8 @@
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Common\Common.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -1,19 +1,108 @@
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Common;
|
||||||
|
using LocalServer.Models;
|
||||||
|
|
||||||
namespace LocalServer;
|
namespace LocalServer;
|
||||||
|
|
||||||
public class LocalSyncServer
|
public class LocalSyncServer
|
||||||
{
|
{
|
||||||
public readonly WebSocket Socket;
|
public StateHelpBase StateHelper;
|
||||||
|
|
||||||
|
public Config? SyncConfig;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发布源连接
|
||||||
|
/// </summary>
|
||||||
|
public readonly WebSocket LocalSocket;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发布源-缓冲区,存储数据 最大1MB
|
||||||
|
/// </summary>
|
||||||
|
public byte[] Buffer = new byte[1024 * 1024];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发布目标-连接
|
||||||
|
/// </summary>
|
||||||
|
public readonly ClientWebSocket RemoteSocket = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发布开始时间
|
||||||
|
/// </summary>
|
||||||
|
private readonly DateTime StartTime = DateTime.Now;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发布名称
|
||||||
|
/// </summary>
|
||||||
public readonly string Name;
|
public readonly string Name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 父工程,用于释放资源
|
||||||
|
/// </summary>
|
||||||
public readonly LocalSyncServerFactory Factory;
|
public readonly LocalSyncServerFactory Factory;
|
||||||
|
|
||||||
public LocalSyncServer(WebSocket socket, string name, LocalSyncServerFactory factory)
|
public LocalSyncServer(WebSocket socket, string name, LocalSyncServerFactory factory)
|
||||||
{
|
{
|
||||||
Socket = socket;
|
LocalSocket = socket;
|
||||||
Name = name;
|
Name = name;
|
||||||
Factory = factory;
|
Factory = factory;
|
||||||
|
StateHelper = new LocalAuthorityState(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task Start()
|
||||||
|
{
|
||||||
|
//最大1MB
|
||||||
|
var buffer = new byte[1024 * 1024];
|
||||||
|
var receiveResult = await LocalSocket.ReceiveAsync(
|
||||||
|
new ArraySegment<byte>(buffer),
|
||||||
|
CancellationToken.None
|
||||||
|
);
|
||||||
|
|
||||||
|
while (!receiveResult.CloseStatus.HasValue)
|
||||||
|
{
|
||||||
|
await LocalSocket.SendAsync(
|
||||||
|
new ArraySegment<byte>(buffer, 0, receiveResult.Count),
|
||||||
|
receiveResult.MessageType,
|
||||||
|
receiveResult.EndOfMessage,
|
||||||
|
CancellationToken.None
|
||||||
|
);
|
||||||
|
|
||||||
|
receiveResult = await LocalSocket.ReceiveAsync(
|
||||||
|
new ArraySegment<byte>(buffer),
|
||||||
|
CancellationToken.None
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Factory.RemoveLocalSyncServer(this);
|
||||||
|
await LocalSocket.CloseAsync(
|
||||||
|
receiveResult.CloseStatus.Value,
|
||||||
|
receiveResult.CloseStatusDescription,
|
||||||
|
CancellationToken.None
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void LocalSocketSendMsg(object msgOb)
|
||||||
|
{
|
||||||
|
string msg = JsonSerializer.Serialize(msgOb);
|
||||||
|
await LocalSocket.SendAsync(
|
||||||
|
new ArraySegment<byte>(Encoding.UTF8.GetBytes(msg)),
|
||||||
|
WebSocketMessageType.Text,
|
||||||
|
true,
|
||||||
|
CancellationToken.None
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void RemoteSocketSendMsg(object msgOb)
|
||||||
|
{
|
||||||
|
string msg = JsonSerializer.Serialize(msgOb);
|
||||||
|
var buffer = AESHelper.EncryptStringToBytes_Aes(msg);
|
||||||
|
await RemoteSocket.SendAsync(
|
||||||
|
buffer,
|
||||||
|
WebSocketMessageType.Text,
|
||||||
|
true,
|
||||||
|
CancellationToken.None
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Close() { }
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,19 +4,29 @@ namespace LocalServer;
|
||||||
|
|
||||||
public class LocalSyncServerFactory
|
public class LocalSyncServerFactory
|
||||||
{
|
{
|
||||||
public void CreateLocalSyncServer(WebSocket socket, string name)
|
private readonly object Lock = new();
|
||||||
|
|
||||||
|
public async void CreateLocalSyncServer(WebSocket socket, string name)
|
||||||
{
|
{
|
||||||
if (Servers.Select(x => x.Name == name).Any())
|
if (Servers.Select(x => x.Name == name).Any())
|
||||||
{
|
{
|
||||||
throw new Exception("there already is a server with that name is Runing!");
|
throw new Exception("there already is a server with that name is Runing!");
|
||||||
}
|
}
|
||||||
Servers.Add(new LocalSyncServer(socket, name, this));
|
var server = new LocalSyncServer(socket, name, this);
|
||||||
|
lock (Lock)
|
||||||
|
{
|
||||||
|
Servers.Add(server);
|
||||||
|
}
|
||||||
|
await server.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<LocalSyncServer> Servers = [];
|
private readonly List<LocalSyncServer> Servers = [];
|
||||||
|
|
||||||
public void RemoveLocalSyncServer(LocalSyncServer server)
|
public void RemoveLocalSyncServer(LocalSyncServer server)
|
||||||
{
|
{
|
||||||
Servers.Remove(server);
|
lock (Lock)
|
||||||
|
{
|
||||||
|
Servers.Remove(server);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
32
Server/LocalServer/Models/Config.cs
Normal file
32
Server/LocalServer/Models/Config.cs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
namespace LocalServer.Models;
|
||||||
|
|
||||||
|
public class DirFileConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 本地-源根目录
|
||||||
|
/// </summary>
|
||||||
|
public required string LocalRootPath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 远程-目标根目录
|
||||||
|
/// </summary>
|
||||||
|
public required string RemoteRootPath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 排除的文件,它是根目录的相对路径
|
||||||
|
/// </summary>
|
||||||
|
public List<string>? ExcludeFiles { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 除此外全部忽略,最高优先级,若有值,ExcludeFiles 将被忽略,它是根目录的相对路径
|
||||||
|
/// </summary>
|
||||||
|
public List<DirFileConfig>? CherryPickFiles { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Config
|
||||||
|
{
|
||||||
|
public required string Name { get; set; }
|
||||||
|
public required string RemoteUrl { get; set; }
|
||||||
|
|
||||||
|
public List<DirFileConfig>? DirFileConfigs { get; set; }
|
||||||
|
}
|
189
Server/LocalServer/StateHelper.cs
Normal file
189
Server/LocalServer/StateHelper.cs
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
|
using System.Net.WebSockets;
|
||||||
|
using System.Runtime.Intrinsics.Arm;
|
||||||
|
using System.Security.AccessControl;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Common;
|
||||||
|
using LocalServer.Models;
|
||||||
|
|
||||||
|
namespace LocalServer;
|
||||||
|
|
||||||
|
// enum StateWhenMsg
|
||||||
|
// {
|
||||||
|
// Authority = 0,
|
||||||
|
// ConfigInfo = 1,
|
||||||
|
// LocalPackAndUpload = 2,
|
||||||
|
// RemoteUnPackAndRelease = 3,
|
||||||
|
// }
|
||||||
|
|
||||||
|
public abstract class StateHelpBase(LocalSyncServer context)
|
||||||
|
{
|
||||||
|
protected readonly LocalSyncServer Context = context;
|
||||||
|
|
||||||
|
public abstract void HandleRemoteMsg(SyncMsg? msg);
|
||||||
|
|
||||||
|
public abstract void HandleLocalMsg(SyncMsg? msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 0. 发布源验证密码
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
public class LocalAuthorityState(LocalSyncServer context) : StateHelpBase(context)
|
||||||
|
{
|
||||||
|
public override void HandleRemoteMsg(SyncMsg? msg)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("error usage!");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void HandleLocalMsg(SyncMsg? msg)
|
||||||
|
{
|
||||||
|
if (msg == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string Pwd = msg.Body;
|
||||||
|
if (Pwd == "Xfs1%$@_fdYU.>>")
|
||||||
|
{
|
||||||
|
Context.LocalSocketSendMsg(new SyncMsg(true, "源服务密码校验成功!"));
|
||||||
|
Context.StateHelper = new WaitingConfigInfoState(Context);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new UnauthorizedAccessException("pwd error!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 1. 获取配置信息,它包含目标的服务器的配置信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
public class WaitingConfigInfoState(LocalSyncServer context) : StateHelpBase(context)
|
||||||
|
{
|
||||||
|
public override void HandleRemoteMsg(SyncMsg? msg) { }
|
||||||
|
|
||||||
|
public override void HandleLocalMsg(SyncMsg? msg)
|
||||||
|
{
|
||||||
|
if (msg == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string ConfigInfo = msg.Body;
|
||||||
|
Context.SyncConfig =
|
||||||
|
JsonSerializer.Deserialize<Config>(ConfigInfo)
|
||||||
|
?? throw new NullReferenceException("ConfigInfo is null");
|
||||||
|
var task = Context.RemoteSocket.ConnectAsync(
|
||||||
|
new Uri(Context.SyncConfig.RemoteUrl),
|
||||||
|
CancellationToken.None
|
||||||
|
);
|
||||||
|
if (task.Wait(10000))
|
||||||
|
{
|
||||||
|
if (Context.RemoteSocket.State == WebSocketState.Open)
|
||||||
|
{
|
||||||
|
var state = new RemoteAuthorityState(Context);
|
||||||
|
state.SendPwdToRemoteServer();
|
||||||
|
Context.StateHelper = state;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("connect remote server failed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new TimeoutException("connect remote server timeout");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 2. 目标服务器权限校验
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
public class RemoteAuthorityState(LocalSyncServer context) : StateHelpBase(context)
|
||||||
|
{
|
||||||
|
public override void HandleRemoteMsg(SyncMsg? msg)
|
||||||
|
{
|
||||||
|
if (msg == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void HandleLocalMsg(SyncMsg? msg) { }
|
||||||
|
|
||||||
|
public void SendPwdToRemoteServer()
|
||||||
|
{
|
||||||
|
var authorityInfo = new
|
||||||
|
{
|
||||||
|
Pwd = "xfs@#123hd??1>>|12#4",
|
||||||
|
MacAdr = new LocalServer.Controllers.LocalServerController(
|
||||||
|
Context.Factory
|
||||||
|
).GetMacAddress()
|
||||||
|
};
|
||||||
|
Context.RemoteSocketSendMsg(authorityInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 3. 文件比较
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
public class DirFilesDiffState(LocalSyncServer context) : StateHelpBase(context)
|
||||||
|
{
|
||||||
|
public override void HandleRemoteMsg(SyncMsg? msg)
|
||||||
|
{
|
||||||
|
if (msg == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void HandleLocalMsg(SyncMsg? msg) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 4. 本地打包并上传
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
public class LocalPackAndUploadState(LocalSyncServer context) : StateHelpBase(context)
|
||||||
|
{
|
||||||
|
public override void HandleRemoteMsg(SyncMsg? msg)
|
||||||
|
{
|
||||||
|
if (msg == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void HandleLocalMsg(SyncMsg? msg) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 5. 目标服务器解包并发布
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
public class RemoteUnPackAndReleaseState(LocalSyncServer context) : StateHelpBase(context)
|
||||||
|
{
|
||||||
|
public override void HandleRemoteMsg(SyncMsg? msg)
|
||||||
|
{
|
||||||
|
if (msg == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void HandleLocalMsg(SyncMsg? msg) { }
|
||||||
|
}
|
Loading…
Reference in a new issue