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 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(); }