Implented SniStream in c# using openssl in c++ buffer data overlap error

86 Views Asked by At

I've implemeneted a stream class which inheretes abstract stream. This class called sniStream which I want to add Sni facility to c#.

The source of sniStream is like this, and it uses a native c++ liberary which I've implemented:

 public class SniStream : Stream
        {
            [DllImport(@"SniReceiver.dll", CallingConvention = CallingConvention.StdCall)]
            private static extern int write_ssl(string data);
    
            [DllImport(@"SniReceiver.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
            private static extern int read_ssl_sni(byte[] data, int offset, int count);
    
            [DllImport(@"SniReceiver.dll", CallingConvention = CallingConvention.StdCall)]
            private static extern void cleanup_openssl();
    
            [DllImport(@"SniReceiver.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
            private static extern IntPtr get_domainname(IntPtr sock);
    
            [DllImport(@"SniReceiver.dll", CallingConvention = CallingConvention.StdCall)]
            private static extern int get_ssl_pending();
    
            [DllImport(@"SniReceiver.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
            private static extern IntPtr load_all_ssl_certificates(string path);
    public SniStream(Stream innerStream, bool leaveInnerStreamOpen)

    {
        innerStream = this;
        CanWrite = CanRead = true;
        Position = 0;
        CanSeek = false;          
    }
    }

 public override long Position { get; set; }


        public override long Length { get; }

        public override bool CanWrite { get; }

        public override bool CanSeek { get; }

        public override bool CanRead { get; }
        public override long Seek(long offset, SeekOrigin origin) => throw new NotImplementedException();

        public override void SetLength(long value) => throw new NotImplementedException();
public override void Write(byte[] buffer, int offset, int count)
        {            
            int writed = write_ssl(Encoding.UTF8.GetString(buffer));
            Position += writed;
            //Array.Clear(buffer, 0, buffer.Length);
        }
public override int Read(byte[] buffer, int offset, int count)
        {
            int received = 0;
            string s = "";                    
            Array.Clear(buffer, 0, buffer.Length);
            received = read_ssl_sni(buffer, offset, count);
            Position += received;
            return received;

        }
 public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
        {
            return new ValueTask(new Task(() => {
                int read= write_ssl(Encoding.UTF8.GetString(buffer.ToArray()));
                Position += read;                
                })
                );
         
        }
 public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
        {
            int revieved = 0;
            string s = "";

            Array.Clear(buffer.ToArray(), 0, buffer.Length);
            revieved=read_ssl_sni(buffer.ToArray(), 0, 0);
            Position += revieved;
            return new ValueTask<int>(revieved);
           

        }
        public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            int revieved = 0;
            string s = "";
     

            Array.Clear(buffer, 0, buffer.Length);
            revieved = read_ssl_sni(buffer, offset, count);
            Position += revieved;
            return Task.FromResult(revieved);
         
        }

        public string GetDomainName(Socket sClient)
        {
            return Marshal.PtrToStringAnsi(get_domainname(sClient.Handle));
        }

        public static string LoadAllCertificates(string path)
        {
            string res = Marshal.PtrToStringAnsi(load_all_ssl_certificates(path));
            return "";
        }

        public override void Flush()
        {
            //cleanup_openssl();
            Position = 0;
        }

The c++ SniReader is like this:

#include "stdafx.h"
#include "SniReceiver.h"

#include <algorithm>
#include <cctype>
#include <string>   


SSL *ssl;
          

SSL_CTX *create_context(SSL_CTX *ctx)
{
    const SSL_METHOD *method;
    method = TLSv1_2_server_method();

    ctx = SSL_CTX_new(method);
    if (!ctx) {
        perror("Unable to create SSL context");
        ERR_print_errors_fp(stderr);
        exit(EXIT_FAILURE);
    }
    return ctx;
}

const char * load_all_ssl_certificates(char* certicatesPath)
{
    //load certificates from a folder
}

void init_openssl()
{
    SSL_load_error_strings();
    OpenSSL_add_ssl_algorithms();
}
void cleanup_openssl()
{
    EVP_cleanup();
    SSL_free(ssl);
}


static int ssl_servername_cb(SSL *s, int *ad, void *arg)
{
    const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
    domainname = servername;
    if (servername != NULL) {
        SSL_CTX * cc = certificates.at(servername);
        SSL_set_SSL_CTX(s, cc);
    }
    return SSL_TLSEXT_ERR_OK;
}

extern "C" __declspec(dllexport) const char * __cdecl get_domainname(DWORD socket)
{
    SSL_CTX *ctx = NULL;
    init_openssl();
    ctx = create_context(ctx);
    SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb);

    ssl = SSL_new(ctx);
    int x = SSL_set_fd(ssl, socket);
    int hand = SSL_do_handshake(ssl);
    int acc = SSL_accept(ssl);

    return domainname;
}


extern "C" __declspec(dllexport) int __stdcall write_ssl(char data[])
{
    int writed=SSL_write(ssl, data, strlen(data));
    return writed;
}

extern "C" __declspec(dllexport) int __stdcall read_ssl_sni(char recvb[],int offset,int count)
{   
    const int recvbuflen = 4096;
    char recvbuf[recvbuflen];       
    return(SSL_read(ssl, recvb, count));            
}

And header file is this:

#include <WinSock2.h>
#include "minwindef.h"
#include <iostream>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <map>

#include <tuple>
#include <filesystem> //New in C++ 17 //old one include <dirent.h> for windows
namespace fs = std::filesystem; // work with vs 2019


std::map<std::string, SSL_CTX *> certificates;

const char *domainname;
char *readResult;


extern "C" __declspec(dllexport) char * __cdecl receive_socket(DWORD socket);

extern "C" __declspec(dllexport) const char * __cdecl load_all_ssl_certificates(char* path);

extern "C" __declspec(dllexport) int __stdcall add_num(int a,int b);

extern "C" __declspec(dllexport) int __stdcall write_ssl(char* data);

extern "C" __declspec(dllexport) int __stdcall read_ssl_sni(char recvbuf[], int offset, int count);

extern "C" __declspec(dllexport) const char * __cdecl get_domainname(DWORD socket);

extern "C" __declspec(dllexport) int __stdcall get_ssl_pending();

extern "C" __declspec(dllexport) void __cdecl cleanup_openssl();

The problem is c# program uses sniStream in async mode. I have 2 pages, one is called fulsh and the other flush2. (flush is just names, not flushing stream).

flush returns flush, and flush2 returns flush2.

when I call each one first and before loading first one, calling second one in new tab in browser, content of second page come before first. I think something in buffer is overlapped. I've read at msdn that implemetors of stream class for async methods should call Read and Write methods inside it.

Any Idea? should I create a byte array which is shared in sniStream as buffer?

enter image description here

enter image description here

0

There are 0 best solutions below