You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Gateway/Mime/HttpMime.cs

450 lines
20 KiB
C#

10 months ago
// using System.IO;
using System.Buffers.Text;
using System;
1 year ago
using System.Collections.Specialized;
1 year ago
using System.Linq;
2 years ago
using System.Net;
1 year ago
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
2 years ago
using System.Threading.Tasks;
10 months ago
using UMC.SshNet;
2 years ago
using UMC.Net;
10 months ago
namespace UMC.ITME
2 years ago
{
public abstract class HttpMime : IDisposable
{
1 year ago
public abstract int Id { get; }
public int ActiveTime
{
get; set;
}
public int TimeOut { protected set; get; } = 20;
1 year ago
2 years ago
public virtual string Scheme => "http";
public void OutputFinish()
{
this.ActiveTime = UMC.Data.Utility.TimeSpan();
this.TimeOut = 20;
this.Request = null;
}
2 years ago
public abstract void Write(byte[] buffer, int offset, int count);
public abstract void Dispose();
public abstract String Host { get; }
public abstract String RemoteIpAddress { get; }
1 year ago
protected MimeRequest Request;
1 year ago
protected void WebSocket(NetContext context)
{
if (context.Tag is NetHttpRequest)
{
var webr = context.Tag as NetHttpRequest;
this.WebSocket(webr);
}
else
{
OutText(403, "WebSocket");
}
}
void WebSocket(NetHttpRequest webRequest)
{
var url = webRequest.Address;
webRequest.Headers["Connection"] = "Upgrade";
10 months ago
var eventArgs = new ConnectAsyncEventArgs
1 year ago
{
10 months ago
Connected = async (client) =>
1 year ago
{
10 months ago
byte[] _data = System.Buffers.ArrayPool<byte>.Shared.Rent(0x600);
1 year ago
10 months ago
try
{
if (url.Scheme == "https")
1 year ago
{
10 months ago
SslStream ssl = new SslStream(new NetworkStream(client, true), false, (sender, certificate, chain, sslPolicyErrors) => true);
await ssl.AuthenticateAsClientAsync(url.Host, null, SslProtocols.None, false);
var textWriter = new TextWriter(ssl.Write, _data);
// textWriter
UMC.Net.NetHttpResponse.Header(webRequest, textWriter);
textWriter.Dispose();
int size = await ssl.ReadAsync(_data, 0, _data.Length);
1 year ago
10 months ago
if (NetBridge.ResponseHeader(_data, 0, size, new NameValueCollection(), out var statucode) && statucode == HttpStatusCode.SwitchingProtocols)
{
this.Request = new HttpsWebSocket(this, ssl);
this.Write(_data, 0, size);
}
else
{
this.Write(_data, 0, size);
this.Dispose();
}
1 year ago
}
else
{
10 months ago
var textWriter = new TextWriter((b, c, s) =>
{
client.Send(b, c, s, SocketFlags.None);
}, _data);
UMC.Net.NetHttpResponse.Header(webRequest, textWriter);
textWriter.Dispose();
var size = client.Receive(_data, 0, _data.Length, SocketFlags.None);
if (NetBridge.ResponseHeader(_data, 0, size, new NameValueCollection(), out var statucode) && statucode == HttpStatusCode.SwitchingProtocols)
{
this.Request = new HttpWebSocket(this, client);
this.Write(_data, 0, size);
}
else
{
this.Write(_data, 0, size);
this.Dispose();
}
}
}
catch (Exception ex)
{
this.OutText(500, ex.ToString());
}
finally
{
System.Buffers.ArrayPool<byte>.Shared.Return(_data);
}
},
Error = (ex) =>
{
this.OutText(500, ex.ToString());
}
};
eventArgs.Start(0, url.Host, url.Port);
HttpMimeServier.httpMimes.TryRemove(this.Id, out var _);
}
void SSH(SshClient sshClient, UMC.Host.HttpWebSocket webSocket, Entities.Device device, NameValueCollection query, String username)
{
sshClient.Connect();
const string terminalName = "vt100";
uint columns = Utility.Parse(query.Get("c"), 80u);
uint rows = Utility.Parse(query.Get("r"), 25u);
uint width = Utility.Parse(query.Get("w"), 640u);
uint height = Utility.Parse(query.Get("h"), 480u);
const int bufferSize = 100;
var inputIndex = 0;
var count = 0;
var input = new char[200];
var isPwd = false;
var isInput = false;
var strea = sshClient.CreateShellStream(terminalName, columns, rows, width, height, bufferSize);
webSocket.Close = () =>
{
sshClient.Disconnect();
strea.Dispose();
};
webSocket.Send("{\"type\":\"info\",\"value\":{\"Title\":\"" + device.Caption + "\",\"Key\":\"" + device.Id + "\"}}");
strea.DataReceived += (b, e) =>
{
var lines = System.Text.Encoding.UTF8.GetString(e.Data).Split("\r\n", StringSplitOptions.RemoveEmptyEntries);
if (lines.Length > 0)
{
var str = lines[lines.Length - 1];
if (isInput)
{
if (lines.Length == 1)
{
var IsTo = false;
var ToValue = -1;
for (var i = 0; i < str.Length; i++)
{
var by = str[i];
switch (by)
{
case '\a':
break;
case '\b':
inputIndex--;
break;
case '\u001b':
if (i + 1 < str.Length)
{
if (str[i + 1] == '[')
{
ToValue = i + 2;
i++;
IsTo = true;
}
}
break;
default:
if (IsTo)
{
if ((by >= 'a' && by <= 'z') || (by >= 'A' && by <= 'Z'))
{
var tv = ToValue == i ? "1" : str.Substring(ToValue, i - ToValue);
switch (by)
{
case 'K':
count = inputIndex;
break;
case 'C':
inputIndex += Utility.IntParse(tv, 1);
break;
case 'D':
inputIndex -= Utility.IntParse(tv, 1);
break;
}
IsTo = false;
}
}
else if (input.Length > inputIndex)
{
input[inputIndex] = by;
inputIndex++;
count = inputIndex;
}
break;
}
}
1 year ago
}
}
else
{
10 months ago
if (str[0] == '[')
1 year ago
{
10 months ago
isInput = device.Username.StartsWith(str, 1, device.Username.Length);
1 year ago
}
else
{
10 months ago
isInput = str.StartsWith(device.Username);
}
if (isPwd && isInput)
{
if (lines.Length == 2)
{
if (lines[0].StartsWith("/"))
{
webSocket.Send("{\"type\":\"view\",\"value\":{\"Path\":\"" + lines[0] + "\",\"Key\":\"" + device.Id + "\"}}");
}
1 year ago
10 months ago
}
1 year ago
}
10 months ago
}
}
webSocket.Send(e.Data, 0, e.Data.Length);
};
webSocket.ReceiveData = (b, c, l) =>
{
if (b[c] == 13)
{
isInput = false;
if (count > 0)
{
var ls = new String(input.AsSpan(0, count));
1 year ago
10 months ago
isPwd = ls == "pwd";
inputIndex = 0;
count = 0;
Activities.SiteSSHActivities.SSHLog(device, username, ls);
1 year ago
}
}
10 months ago
try
{
strea.Write(b, c, l);
strea.Flush();
}
catch
1 year ago
{
10 months ago
webSocket.Disconnect();
1 year ago
}
};
10 months ago
strea.ErrorOccurred += (e, b) =>
1 year ago
{
10 months ago
sshClient.Disconnect();
strea.Dispose();
1 year ago
};
}
1 year ago
public virtual void PrepareRespone(HttpMimeRequest request)
2 years ago
{
this.TimeOut = 300;
try
2 years ago
{
1 year ago
if (request.IsWebSocket)
2 years ago
{
if (request.RawUrl.StartsWith("/UMC.WS/"))
{
10 months ago
var Path = request.Url.AbsolutePath.Substring(8);
var ds = request.Cookies.GetValues(WebServlet.SessionCookieName) ?? new string[] { Path };
if (ds.Length > 0)
{
1 year ago
string secWebSocketKey = request.Headers["Sec-WebSocket-Key"];
if (String.IsNullOrEmpty(secWebSocketKey) == false)
{
1 year ago
var buffers = System.Buffers.ArrayPool<byte>.Shared.Rent(0x200);
10 months ago
Guid dID;
1 year ago
10 months ago
if (ds.Any(r => r == Path))
{
ds[0] = Path;
dID = UMC.Data.Utility.Guid(Path, true).Value;
}
else
{
dID = SiteConfig.MD5Key(String.Join(',', ds), Path);
}
var webr = new UMC.Host.HttpWebSocket(this.Write, dID, this.Dispose);
1 year ago
this.Request = webr;
1 year ago
var size = secWebSocketKey.WriteBytes(buffers, 0);
size += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11".WriteBytes(buffers, size);
int len = System.Security.Cryptography.SHA1.HashData(buffers.AsSpan(0, size), buffers.AsSpan(size, 24));
string secWebSocketAcceptString = Convert.ToBase64String(buffers.AsSpan(size, len));
var writer = new Net.TextWriter(request._context.Write, buffers);
writer.Write($"HTTP/1.1 101 {HttpStatusDescription.Get(101)}\r\n");
writer.Write("Connection: Upgrade\r\n");
writer.Write("Upgrade: websocket\r\n");
writer.Write($"Sec-WebSocket-Accept: {secWebSocketAcceptString}\r\n");
1 year ago
writer.Write("Server: ITME\r\n\r\n");
writer.Flush();
writer.Dispose();
1 year ago
System.Buffers.ArrayPool<byte>.Shared.Return(buffers);
HttpMimeServier.httpMimes.TryRemove(this.Id, out var _);
10 months ago
var devices = Path.Split('/', StringSplitOptions.RemoveEmptyEntries);
if (devices.Length > 1)
1 year ago
{
10 months ago
var deviceId = Data.Utility.Guid(ds[0], true).Value;
var session = new Data.Session<Data.AccessToken>(deviceId.ToString());
if (session.Value != null && session.Value.Device == deviceId)
1 year ago
{
10 months ago
if (session.Value.IsInRole(UMC.Security.Membership.UserRole))
{
if (Security.AuthManager.Authorization(session.Value.Identity(), 0, $"WebSSH/{devices[1]}", out var isBiometric) > 0)
{
if (isBiometric && session.Value.BiometricTime == 0)
{
var seesionKey = UMC.Data.Utility.Guid(session.Value.Device.Value);
var url = $"/Biometric?oauth_callback={Uri.EscapeDataString(request.Url.AbsoluteUri)}&transfer={seesionKey}";
webr.Send("{\"type\":\"url\",\"value\":" + url + "\"}}");
}
else
{
var device = UMC.Data.HotCache.Get(new Entities.Device { Id = Utility.IntParse(devices[1], 0) });
if (device != null)
{
var us = UMC.Data.License.GetLicense("WebSSH", 5);
Data.Caches.ICacheSet cacheSet2 = UMC.Data.HotCache.Cache<Entities.Device>();
if ((cacheSet2.Count > us.Quantity && us.Quantity > 0) || (us.ExpireTime > 0 && us.ExpireTime < Utility.TimeSpan()))
{
webr.Send("{\"type\":\"license\",\"msg\":\"设备数量超限,请保持合规\"}");
// return;
webr.Disconnect();
return;
}
var password = UMC.Data.DataFactory.Instance().Password(SiteConfig.MD5Key(device.Ip, device.Username));
var ssh = new SshClient(device.Ip, device.Port ?? 22, device.Username, password);
var _QueryString = System.Web.HttpUtility.ParseQueryString(request.Url.Query);
webr.Send("{\"type\":\"device\",\"value\":\"" + Utility.Guid(dID) + "\"}");
this.SSH(ssh, webr, device, _QueryString, session.Value.Username);
return;
}
}
}
}
1 year ago
}
}
10 months ago
var lic = UMC.Data.License.GetLicense("UserSession", 1000);
Data.Caches.ICacheSet cacheSet = UMC.Data.HotCache.Cache<UMC.Data.Entities.Session>();
if ((cacheSet.Count > lic.Quantity && lic.Quantity > 0) || (lic.ExpireTime > 0 && lic.ExpireTime < Utility.TimeSpan()))
{
webr.Send("{\"msg\":\"会话规模超限,请保持合规\"}");
}
}
else
{
OutText(403, "not validate websocket headers");
}
}
else
{
1 year ago
OutText(403, "Permission denied");
}
}
else
{
var context = new HttpMimeContext(request, new HttpMimeResponse(this, request));
context.ProcessRequest();
this.WebSocket(context);
}
2 years ago
}
else
{
var context = new HttpMimeContext(request, new HttpMimeResponse(this, request));
context.ProcessRequest();
context.ProcessAfter();
2 years ago
}
}
catch (Exception ex)
{
OutText(500, "text/plain", ex.ToString());
}
2 years ago
}
public void OutText(int status, string contentType, String text)
{
var writer = new TextWriter(this.Write);
writer.Write($"HTTP/1.1 {status} {HttpStatusDescription.Get(status)}\r\n");
writer.Write($"Content-Type: {contentType}; charset=utf-8\r\n");
writer.Write($"Content-Length: {System.Text.Encoding.UTF8.GetByteCount(text)}\r\n");
writer.Write("Connection: close\r\n");
10 months ago
writer.Write("Server: ITME\r\n\r\n");
2 years ago
writer.Write(text);
writer.Flush();
writer.Close();
this.Dispose();
}
public void OutText(int status, String text)
{
this.OutText(status, "text/plain", text);
}
}
}