shoko/Protocols/lib/Gemini.cs

122 lines
3.6 KiB
C#

using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
namespace Shoko;
class Gemini
{
public Uri Url { get; set; }
public X509CertificateCollection ClientCertificates = null;
public X509Certificate ServerCertificate
{
get => _serverCertificate;
}
X509Certificate _serverCertificate = null;
TcpClient client;
public SslStream sslStream;
public Gemini(string url, X509CertificateCollection certificates)
{
Url = new Uri(url);
ClientCertificates = certificates;
}
public Gemini(Uri url, X509CertificateCollection certificates)
{
Url = url;
ClientCertificates = certificates;
}
public Gemini(string url)
{
Url = new Uri(url);
}
public Gemini(Uri url)
{
Url = url;
}
bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
/* if (sslPolicyErrors == SslPolicyErrors.None)
return true; */
// TODO: validate user certificates
//Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
_serverCertificate = certificate;
return true;
}
public void Connect()
{
var port = Url.Port;
if(port < 0) port = 1965;
client = new TcpClient(Url.DnsSafeHost, port);
sslStream = new SslStream(client.GetStream(), false,
new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
try
{
sslStream.AuthenticateAsClient(new SslClientAuthenticationOptions{
TargetHost = Url.Host,
ClientCertificates = ClientCertificates,
EnabledSslProtocols = SslProtocols.Tls12,
//ApplicationProtocols = { new SslApplicationProtocol(), new SslApplicationProtocol("gemini") }
});
}
catch(AuthenticationException)
{
client.Close();
throw;
}
byte[] message = Encoding.UTF8.GetBytes(Url.AbsoluteUri+"\r\n");
sslStream.Write(message);
sslStream.Flush();
}
public string ReadHeader()
{
StringBuilder message = new StringBuilder();
int b;
do
{
b = sslStream.ReadByte();
message.Append((char)b);
} while(b != 10 && b != -1);
return message.ToString();
}
public int Read(byte[] buffer, int offset, int count) => sslStream.Read(buffer, offset, count);
public string ReadAll()
{
byte[] buffer = new byte[2048];
StringBuilder messageData = new StringBuilder();
int bytes = -1;
do
{
bytes = Read(buffer, 0, buffer.Length);
Decoder decoder = Encoding.UTF8.GetDecoder();
char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
decoder.GetChars(buffer, 0, bytes, chars, 0);
messageData.Append(chars);
} while (bytes != 0);
return messageData.ToString();
}
public string ReadBase64()
{
IEnumerable<byte> array = new byte[0];
int bytes = -1;
byte[] buffer = new byte[2048];
do
{
bytes = Read(buffer, 0, buffer.Length);
var buffer2 = new byte[bytes];
Array.Copy(buffer, buffer2, bytes);
array = array.Concat(buffer2);
} while (bytes != 0);
return Convert.ToBase64String(array.ToArray());
}
public void Close() => client.Close();
}