mirror of
https://github.com/danieleteti/delphimvcframework.git
synced 2024-11-16 08:15:53 +01:00
391 lines
9.2 KiB
ObjectPascal
391 lines
9.2 KiB
ObjectPascal
{******************************************************************************}
|
|
{ }
|
|
{ Delphi cross platform socket library }
|
|
{ }
|
|
{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) }
|
|
{ }
|
|
{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket }
|
|
{ }
|
|
{******************************************************************************}
|
|
unit Net.MbedBIO;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.SysUtils,
|
|
System.IOUtils,
|
|
Net.MbedTls;
|
|
|
|
const
|
|
SSL_BIO_ERROR = -1;
|
|
SSL_BIO_UNSET = -2;
|
|
SSL_BIO_SIZE = 16384; // default BIO write size if not set
|
|
|
|
SSL_FAILED = -1;
|
|
SSL_SUCCESS = 0;
|
|
|
|
// BIO_TYPE
|
|
BIO_BUFFER = 1;
|
|
BIO_SOCKET = 2;
|
|
BIO_SSL = 3;
|
|
BIO_MEMORY = 4;
|
|
BIO_BIO = 5;
|
|
BIO_FILE = 6;
|
|
|
|
type
|
|
PBIO = ^BIO;
|
|
|
|
BIO = record
|
|
prev: PBIO; // previous in chain
|
|
next: PBIO; // next in chain
|
|
pair: PBIO; // BIO paired with
|
|
mem: PByte; // memory buffer
|
|
wrSz: Integer; // write buffer size (mem)
|
|
wrIdx: Integer; // current index for write buffer
|
|
rdIdx: Integer; // current read index
|
|
readRq: Integer; // read request
|
|
memLen: Integer; // memory buffer length
|
|
&type: Integer; // method type
|
|
end;
|
|
|
|
// 抽象 IO API
|
|
function SSL_BIO_new(&type: Integer): PBIO;
|
|
function BIO_make_bio_pair(b1, b2: PBIO): Integer;
|
|
|
|
function BIO_ctrl_pending(BIO: PBIO): Integer;
|
|
function BIO_set_write_buf_size(BIO: PBIO; size: Integer): Integer;
|
|
|
|
function BIO_read(BIO: PBIO; buf: Pointer; size: Integer): Integer;
|
|
function BIO_write(BIO: PBIO; buf: Pointer; size: Integer): Integer;
|
|
|
|
function BIO_free(BIO: PBIO): Integer;
|
|
function BIO_free_all(BIO: PBIO): Integer;
|
|
|
|
function BIO_net_recv(ctx: Pointer; buf: Pointer; size: Size_T): Integer; cdecl;
|
|
function BIO_net_send(ctx: Pointer; buf: Pointer; size: Size_T): Integer; cdecl;
|
|
|
|
implementation
|
|
|
|
// Return the number of pending bytes in read and write buffers
|
|
function BIO_ctrl_pending(BIO: PBIO): Integer;
|
|
var
|
|
pair: PBIO;
|
|
begin
|
|
if (BIO = nil) then
|
|
Exit(0);
|
|
|
|
if (BIO.&type = BIO_MEMORY) then
|
|
Exit(BIO.memLen);
|
|
|
|
// type BIO_BIO then check paired buffer
|
|
if (BIO.&type = BIO_BIO) and (BIO.pair <> nil) then
|
|
begin
|
|
pair := BIO.pair;
|
|
if (pair.wrIdx > 0) and (pair.wrIdx <= pair.rdIdx) then
|
|
// in wrap around state where begining of buffer is being overwritten
|
|
Exit(pair.wrSz - pair.rdIdx + pair.wrIdx)
|
|
else
|
|
// simple case where has not wrapped around
|
|
Exit(pair.wrIdx - pair.rdIdx);
|
|
end;
|
|
|
|
Result := 0;
|
|
end;
|
|
|
|
function BIO_set_write_buf_size(BIO: PBIO; size: Integer): Integer;
|
|
begin
|
|
if (BIO = nil) or (BIO.&type <> BIO_BIO) or (size < 0) then
|
|
Exit(SSL_FAILED);
|
|
|
|
// if already in pair then do not change size
|
|
if BIO.pair <> nil then
|
|
Exit(SSL_FAILED);
|
|
|
|
BIO.wrSz := size;
|
|
if (BIO.wrSz < 0) then
|
|
Exit(SSL_FAILED);
|
|
|
|
if (BIO.mem <> nil) then
|
|
FreeMem(BIO.mem);
|
|
|
|
GetMem(BIO.mem, BIO.wrSz);
|
|
if (BIO.mem = nil) then
|
|
Exit(SSL_FAILED);
|
|
|
|
BIO.wrIdx := 0;
|
|
BIO.rdIdx := 0;
|
|
Result := SSL_SUCCESS;
|
|
end;
|
|
|
|
{ * Joins two BIO_BIO types. The write of b1 goes to the read of b2 and vise
|
|
* versa. Creating something similar to a two way pipe.
|
|
* Reading and writing between the two BIOs is not thread safe, they are
|
|
* expected to be used by the same thread.
|
|
* }
|
|
function BIO_make_bio_pair(b1, b2: PBIO): Integer;
|
|
begin
|
|
if (b1 = nil) or (b2 = nil) then
|
|
Exit(SSL_FAILED);
|
|
|
|
// both are expected to be of type BIO and not already paired
|
|
if (b1.&type <> BIO_BIO)
|
|
or (b2.&type <> BIO_BIO) or (b1.pair <> nil) or (b2.pair <> nil) then
|
|
Exit(SSL_FAILED);
|
|
|
|
// set default write size if not already set
|
|
if (b1.mem = nil)
|
|
and (BIO_set_write_buf_size(b1, SSL_BIO_SIZE) <> SSL_SUCCESS) then
|
|
Exit(SSL_FAILED);
|
|
|
|
if (b2.mem = nil)
|
|
and (BIO_set_write_buf_size(b2, SSL_BIO_SIZE) <> SSL_SUCCESS) then
|
|
Exit(SSL_FAILED);
|
|
|
|
b1.pair := b2;
|
|
b2.pair := b1;
|
|
Result := SSL_SUCCESS;
|
|
end;
|
|
|
|
// Does not advance read index pointer
|
|
function BIO_nread0(BIO: PBIO; buf: PPointer): Integer;
|
|
var
|
|
pair: PBIO;
|
|
begin
|
|
if (BIO = nil) or (buf = nil) then
|
|
Exit(0);
|
|
|
|
// if paired read from pair
|
|
if (BIO.pair <> nil) then
|
|
begin
|
|
pair := BIO.pair;
|
|
|
|
// case where have wrapped around write buffer
|
|
buf^ := pair.mem + pair.rdIdx;
|
|
if (pair.wrIdx > 0) and (pair.rdIdx >= pair.wrIdx) then
|
|
Exit(pair.wrSz - pair.rdIdx)
|
|
else
|
|
Exit(pair.wrIdx - pair.rdIdx);
|
|
end;
|
|
|
|
Result := 0;
|
|
end;
|
|
|
|
function BIO_nread(BIO: PBIO; buf: PPointer; num: Integer): Integer;
|
|
var
|
|
sz: Integer;
|
|
begin
|
|
sz := SSL_BIO_UNSET;
|
|
if (BIO = nil) or (buf = nil) then
|
|
Exit(SSL_FAILED);
|
|
|
|
if (BIO.pair <> nil) then
|
|
begin
|
|
// special case if asking to read 0 bytes
|
|
if (num = 0) then
|
|
begin
|
|
buf^ := BIO.pair.mem + BIO.pair.rdIdx;
|
|
Exit(0);
|
|
end;
|
|
|
|
// get amount able to read and set buffer pointer
|
|
sz := BIO_nread0(BIO, buf);
|
|
if (sz = 0) then
|
|
Exit(SSL_BIO_ERROR);
|
|
|
|
if (num < sz) then
|
|
sz := num;
|
|
|
|
BIO.pair.rdIdx := BIO.pair.rdIdx + sz;
|
|
|
|
// check if have read to the end of the buffer and need to reset
|
|
if (BIO.pair.rdIdx = BIO.pair.wrSz) then
|
|
begin
|
|
BIO.pair.rdIdx := 0;
|
|
if (BIO.pair.wrIdx = BIO.pair.wrSz) then
|
|
BIO.pair.wrIdx := 0;
|
|
end;
|
|
|
|
// check if read up to write index, if so then reset indexs
|
|
if (BIO.pair.rdIdx = BIO.pair.wrIdx) then
|
|
begin
|
|
BIO.pair.rdIdx := 0;
|
|
BIO.pair.wrIdx := 0;
|
|
end;
|
|
end;
|
|
|
|
Result := sz;
|
|
end;
|
|
|
|
function BIO_nwrite(BIO: PBIO; buf: PPointer; num: Integer): Integer;
|
|
var
|
|
sz: Integer;
|
|
begin
|
|
sz := SSL_BIO_UNSET;
|
|
if (BIO = nil) or (buf = nil) then
|
|
Exit(0);
|
|
|
|
if (BIO.pair <> nil) then
|
|
begin
|
|
if num = 0 then
|
|
begin
|
|
buf^ := BIO.mem + BIO.wrIdx;
|
|
Exit(0);
|
|
end;
|
|
if (BIO.wrIdx < BIO.rdIdx) then
|
|
// if wrapped around only write up to read index. In this case
|
|
// rdIdx is always greater then wrIdx so sz will not be negative.
|
|
sz := BIO.rdIdx - BIO.wrIdx
|
|
else if (BIO.rdIdx > 0) and (BIO.wrIdx = BIO.rdIdx) then
|
|
Exit(SSL_BIO_ERROR) // no more room to write
|
|
else
|
|
begin
|
|
// write index is past read index so write to end of buffer
|
|
sz := BIO.wrSz - BIO.wrIdx;
|
|
if (sz <= 0) then
|
|
begin
|
|
// either an error has occured with write index or it is at the
|
|
// end of the write buffer.
|
|
if (BIO.rdIdx = 0) then
|
|
// no more room, nothing has been read
|
|
Exit(SSL_BIO_ERROR);
|
|
|
|
BIO.wrIdx := 0;
|
|
|
|
// check case where read index is not at 0
|
|
if (BIO.rdIdx > 0) then
|
|
sz := BIO.rdIdx // can write up to the read index
|
|
else
|
|
sz := BIO.wrSz; // no restriction other then buffer size
|
|
end;
|
|
end;
|
|
|
|
if (num < sz) then
|
|
sz := num;
|
|
|
|
buf^ := BIO.mem + BIO.wrIdx;
|
|
BIO.wrIdx := BIO.wrIdx + sz;
|
|
|
|
// if at the end of the buffer and space for wrap around then set
|
|
// write index back to 0
|
|
if (BIO.wrIdx = BIO.wrSz) and (BIO.rdIdx > 0) then
|
|
BIO.wrIdx := 0;
|
|
end;
|
|
|
|
Result := sz;
|
|
end;
|
|
|
|
// Reset BIO to initial state
|
|
function BIO_reset(BIO: PBIO): Integer;
|
|
begin
|
|
if (BIO = nil) then
|
|
// -1 is consistent failure even for FILE type
|
|
Exit(SSL_BIO_ERROR);
|
|
|
|
case BIO.&type of
|
|
BIO_BIO:
|
|
begin
|
|
BIO.rdIdx := 0;
|
|
BIO.wrIdx := 0;
|
|
Exit(0);
|
|
end;
|
|
end;
|
|
|
|
Result := SSL_BIO_ERROR;
|
|
end;
|
|
|
|
function BIO_read(BIO: PBIO; buf: Pointer; size: Integer): Integer;
|
|
var
|
|
sz: Integer;
|
|
pt: PPointer;
|
|
begin
|
|
sz := BIO_nread(BIO, @pt, size);
|
|
if (sz > 0) then
|
|
Move(pt^, buf^, sz);
|
|
|
|
Result := sz;
|
|
end;
|
|
|
|
function BIO_write(BIO: PBIO; buf: Pointer; size: Integer): Integer;
|
|
var
|
|
sz: Integer;
|
|
data: Pointer;
|
|
begin
|
|
// internal function where arguments have already been sanity checked
|
|
sz := BIO_nwrite(BIO, @data, size);
|
|
|
|
// test space for write
|
|
if (sz <= 0) then
|
|
Exit(sz);
|
|
|
|
Move(buf^, data^, sz);
|
|
Result := sz;
|
|
end;
|
|
|
|
// support bio type only
|
|
function SSL_BIO_new(&type: Integer): PBIO;
|
|
begin
|
|
GetMem(Result, SizeOf(BIO));
|
|
FillChar(Result^, SizeOf(BIO), 0);
|
|
Result.&type := &type;
|
|
end;
|
|
|
|
function BIO_free(BIO: PBIO): Integer;
|
|
begin
|
|
// unchain?, doesn't matter in goahead since from free all
|
|
if (BIO <> nil) then
|
|
begin
|
|
// remove from pair by setting the paired bios pair to nil
|
|
if (BIO.pair <> nil) then
|
|
BIO.pair.pair := nil;
|
|
|
|
if (BIO.mem <> nil) then
|
|
FreeMem(BIO.mem);
|
|
|
|
FreeMem(BIO);
|
|
end;
|
|
|
|
Result := 0;
|
|
end;
|
|
|
|
function BIO_free_all(BIO: PBIO): Integer;
|
|
var
|
|
next: PBIO;
|
|
begin
|
|
while (BIO <> nil) do
|
|
begin
|
|
next := BIO.next;
|
|
BIO_free(BIO);
|
|
BIO := next;
|
|
end;
|
|
|
|
Result := 0;
|
|
end;
|
|
|
|
function BIO_net_send(ctx: Pointer; buf: Pointer; size: Size_T): Integer;
|
|
var
|
|
BIO: PBIO;
|
|
sz: Integer;
|
|
begin
|
|
BIO := PBIO(ctx);
|
|
sz := BIO_write(BIO, buf, size);
|
|
if (sz <= 0) then
|
|
Exit(MBEDTLS_ERR_SSL_WANT_WRITE);
|
|
|
|
Result := sz;
|
|
end;
|
|
|
|
function BIO_net_recv(ctx: Pointer; buf: Pointer; size: Size_T): Integer;
|
|
var
|
|
BIO: PBIO;
|
|
sz: Integer;
|
|
begin
|
|
BIO := PBIO(ctx);
|
|
sz := BIO_read(BIO, buf, size);
|
|
if (sz <= 0) then
|
|
Exit(MBEDTLS_ERR_SSL_WANT_READ);
|
|
|
|
Result := sz;
|
|
end;
|
|
|
|
end.
|