Added logging support and refactored some code
Log4J vuln inspired me to add some logging to this project :)))
This commit is contained in:
@@ -8,35 +8,30 @@ using System.Text;
|
||||
using System.Security.Authentication;
|
||||
using System.IO;
|
||||
|
||||
using RequestCallback = System.Action<Cuipod.Request, Cuipod.Response>;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Cuipod
|
||||
{
|
||||
using RequestCallback = System.Action<Cuipod.Request, Cuipod.Response, ILogger<App>>;
|
||||
|
||||
public class App
|
||||
{
|
||||
private readonly TcpListener _listener = new TcpListener(IPAddress.Any, 1965);
|
||||
private readonly Dictionary<string, RequestCallback> _requestCallbacks = new Dictionary<string, RequestCallback>();
|
||||
private readonly byte[] _buffer = new byte[4096];
|
||||
private readonly Decoder _decoder = Encoding.UTF8.GetDecoder();
|
||||
|
||||
private readonly string _directoryToServe;
|
||||
private readonly TcpListener _listener;
|
||||
private readonly X509Certificate2 _serverCertificate;
|
||||
private readonly Dictionary<string, RequestCallback> _requestCallbacks;
|
||||
private readonly ILogger<App> _logger;
|
||||
|
||||
private RequestCallback _onBadRequestCallback;
|
||||
|
||||
//somewhat flaky implementation - probably deprecate it
|
||||
public App(string directoryToServe, string certificateFile, string privateRSAKeyFilePath)
|
||||
public App(string directoryToServe, X509Certificate2 certificate, ILogger<App> logger)
|
||||
{
|
||||
_directoryToServe = directoryToServe;
|
||||
_listener = new TcpListener(IPAddress.Any, 1965);
|
||||
_requestCallbacks = new Dictionary<string, RequestCallback>();
|
||||
_serverCertificate = CertificateUtils.LoadCertificate(certificateFile, privateRSAKeyFilePath);
|
||||
}
|
||||
|
||||
public App(string directoryToServe, X509Certificate2 certificate)
|
||||
{
|
||||
|
||||
_directoryToServe = directoryToServe;
|
||||
_listener = new TcpListener(IPAddress.Any, 1965);
|
||||
_requestCallbacks = new Dictionary<string, RequestCallback>();
|
||||
_serverCertificate = certificate;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void OnRequest(string route, RequestCallback callback)
|
||||
@@ -49,13 +44,14 @@ namespace Cuipod
|
||||
_onBadRequestCallback = callback;
|
||||
}
|
||||
|
||||
public int Run()
|
||||
public void Run()
|
||||
{
|
||||
int status = 0;
|
||||
Console.WriteLine("Serving capsule on 0.0.0.0:1965");
|
||||
try
|
||||
{
|
||||
_listener.Start();
|
||||
|
||||
_logger.LogInformation("Serving capsule on {0}", _listener.Server.LocalEndPoint.ToString());
|
||||
|
||||
while (true)
|
||||
{
|
||||
ProcessRequest(_listener.AcceptTcpClient());
|
||||
@@ -63,15 +59,12 @@ namespace Cuipod
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
Console.WriteLine("SocketException: {0}", e);
|
||||
status = 1;
|
||||
_logger.LogError("SocketException: {0}", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_listener.Stop();
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
private void ProcessRequest(TcpClient client)
|
||||
@@ -86,16 +79,16 @@ namespace Cuipod
|
||||
}
|
||||
catch (AuthenticationException e)
|
||||
{
|
||||
Console.WriteLine("Exception: {0}", e.Message);
|
||||
_logger.LogError("AuthenticationException: {0}", e.Message);
|
||||
if (e.InnerException != null)
|
||||
{
|
||||
Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
|
||||
_logger.LogError("Inner exception: {0}", e.InnerException.Message);
|
||||
}
|
||||
Console.WriteLine("Authentication failed - closing the connection.");
|
||||
_logger.LogError("Authentication failed - closing the connection.");
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
Console.WriteLine("Exception: {0}", e.Message);
|
||||
_logger.LogError("IOException: {0}", e.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -110,33 +103,37 @@ namespace Cuipod
|
||||
sslStream.AuthenticateAsServer(_serverCertificate, false, SslProtocols.Tls12 | SslProtocols.Tls13, false);
|
||||
|
||||
// Read a message from the client.
|
||||
string rawURL = ReadRequest(sslStream);
|
||||
string rawRequest = ReadRequest(sslStream);
|
||||
|
||||
Response response = new Response(_directoryToServe);
|
||||
|
||||
if (rawURL == null)
|
||||
if (rawRequest == null)
|
||||
{
|
||||
_logger.LogDebug("rawRequest is null - bad request");
|
||||
response.Status = StatusCode.BadRequest;
|
||||
return response;
|
||||
}
|
||||
|
||||
Console.WriteLine(rawURL);
|
||||
_logger.LogDebug("Raw request: \"{0}\"", rawRequest);
|
||||
|
||||
int protocolDelimiter = rawURL.IndexOf("://");
|
||||
const string protocol= "gemini";
|
||||
const string protocolSeparator = "://";
|
||||
|
||||
int protocolDelimiter = rawRequest.IndexOf(protocolSeparator);
|
||||
if (protocolDelimiter == -1)
|
||||
{
|
||||
response.Status = StatusCode.BadRequest;
|
||||
return response;
|
||||
}
|
||||
|
||||
string protocol = rawURL.Substring(0, protocolDelimiter);
|
||||
if (protocol != "gemini")
|
||||
string requestProtocol = rawRequest.Substring(0, protocolDelimiter);
|
||||
if (requestProtocol != protocol)
|
||||
{
|
||||
response.Status = StatusCode.BadRequest;
|
||||
return response;
|
||||
}
|
||||
|
||||
string url = rawURL.Substring(protocolDelimiter + 3);
|
||||
string url = rawRequest.Substring(protocolDelimiter + protocolSeparator.Length);
|
||||
int domainNameDelimiter = url.IndexOf("/");
|
||||
if (domainNameDelimiter == -1)
|
||||
{
|
||||
@@ -144,22 +141,38 @@ namespace Cuipod
|
||||
return response;
|
||||
}
|
||||
string domainName = url.Substring(0, domainNameDelimiter);
|
||||
string baseURL = protocol + protocolSeparator + domainName;
|
||||
|
||||
Request request = new Request("gemini://" + domainName , url.Substring(domainNameDelimiter));
|
||||
string route = url.Substring(domainNameDelimiter);
|
||||
string parameters = "";
|
||||
int parametersDelimiter = route.IndexOf("?");
|
||||
if (parametersDelimiter != -1)
|
||||
{
|
||||
parameters = route.Substring(parametersDelimiter + 1);
|
||||
route = route.Substring(0, parametersDelimiter);
|
||||
}
|
||||
|
||||
_logger.LogDebug("Request info:");
|
||||
_logger.LogDebug("\tBaseURL: \"{0}\"", baseURL);
|
||||
_logger.LogDebug("\tRoute: \"{0}\"", route);
|
||||
_logger.LogDebug("\tParameters: \"{0}\"", parameters);
|
||||
|
||||
Request request = new Request(baseURL, route, parameters);
|
||||
if (response.Status == StatusCode.Success)
|
||||
{
|
||||
RequestCallback callback;
|
||||
_requestCallbacks.TryGetValue(request.Route, out callback);
|
||||
if (callback != null)
|
||||
{
|
||||
callback(request, response);
|
||||
callback(request, response, _logger);
|
||||
}
|
||||
else if (_onBadRequestCallback != null)
|
||||
{
|
||||
_onBadRequestCallback(request, response);
|
||||
_onBadRequestCallback(request, response, _logger);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Bad request: No suitable request callback");
|
||||
response.Status = StatusCode.BadRequest;
|
||||
return response;
|
||||
}
|
||||
@@ -170,13 +183,10 @@ namespace Cuipod
|
||||
|
||||
private string ReadRequest(SslStream sslStream)
|
||||
{
|
||||
byte[] buffer = new byte[2048];
|
||||
Decoder decoder = Encoding.UTF8.GetDecoder();
|
||||
|
||||
StringBuilder requestData = new StringBuilder();
|
||||
int bytes = sslStream.Read(buffer, 0, buffer.Length);
|
||||
char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
|
||||
decoder.GetChars(buffer, 0, bytes, chars, 0);
|
||||
int bytes = sslStream.Read(_buffer, 0, _buffer.Length);
|
||||
char[] chars = new char[_decoder.GetCharCount(_buffer, 0, bytes)];
|
||||
_decoder.GetChars(_buffer, 0, bytes, chars, 0);
|
||||
string line = new string(chars);
|
||||
if (line.EndsWith("\r\n"))
|
||||
{
|
||||
|
||||
@@ -4,4 +4,8 @@
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Cuipod
|
||||
namespace Cuipod
|
||||
{
|
||||
public class Request
|
||||
{
|
||||
@@ -10,20 +6,11 @@ namespace Cuipod
|
||||
public string Route { get; internal set; }
|
||||
public string Parameters { get; internal set; }
|
||||
|
||||
public Request(string baseURL, string route)
|
||||
internal Request(string baseURL, string route, string parameters)
|
||||
{
|
||||
BaseURL = baseURL;
|
||||
|
||||
int parametersDelimiter = route.IndexOf("?");
|
||||
if (parametersDelimiter != -1)
|
||||
{
|
||||
Parameters = route.Substring(parametersDelimiter + 1);
|
||||
Route = route.Substring(0, parametersDelimiter);
|
||||
}
|
||||
else
|
||||
{
|
||||
Route = route;
|
||||
}
|
||||
Route = route;
|
||||
Parameters = parameters;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@ namespace Cuipod
|
||||
{
|
||||
public StatusCode Status { get; set; }
|
||||
|
||||
private string _directoryToServe;
|
||||
private readonly string _directoryToServe;
|
||||
private string _requestBody = "";
|
||||
|
||||
public Response(string directoryToServe)
|
||||
internal Response(string directoryToServe)
|
||||
{
|
||||
_directoryToServe = directoryToServe;
|
||||
Status = StatusCode.Success;
|
||||
|
||||
Reference in New Issue
Block a user