Windows新异步网络编程模型5 RIO-socks5服务端

unit RioSocks5Server;
{
//chatGPT写的 编译没通过还要调整

调用例子
uses
  Winapi.Winsock2, System.SysUtils, RioSocks5Server, RioProxyServer;

var
  Socks5Server: TRioSocks5Server;
  ProxyServer: TRioProxyServer;
begin
  Socks5Server := TRioSocks5Server.Create;
  ProxyServer := TRioProxyServer.Create;
  try
    Socks5Server.Start('0.0.0.0', 1080, 1024);
    ProxyServer.Start('0.0.0.0', 0, 1024);
    ProxyServer.OnProxyComplete := Socks5Server.OnProxyComplete;
    ProxyServer.AuthRequired := True;
    ProxyServer.Authenticator := TMyAuthenticator.Create;
    TMyAuthenticator(ProxyServer.Authenticator).AddUser('username', 'password');
    while True do
    begin
      Sleep(1000);
    end;
  finally
    Socks5Server.Free;
    ProxyServer.Free;
  end;
end;

}

interface

uses
  System.SysUtils,
  System.Classes,
  System.SyncObjs,
  System.Win.ScktComp,
  Winapi.Windows,
  Winapi.WinSock2,
  IdWinsock2,
  Soap.Rio;

type
  TRioSocks5Server = class
  private
    FRioHandle: THandle;
    FRioCompletionPort: THandle;
    FServerSocket: TServerSocket;
    FNumBuffers: Integer;
    FBufferSize: Integer;
    FOverlappedArray: array of TOverlapped;
    FBufferArray: array of Pointer;
    FAcceptThread: TThread;
    FClientThreads: TThreadList;
    FUserName: string;
    FPassword: string;
    procedure CreateRIO;
    procedure DestroyRIO;
    procedure OnClientConnect(Sender: TObject; Socket: TCustomWinSocket);
    procedure OnClientRead(Sender: TObject; Socket: TCustomWinSocket);
    procedure OnClientDisconnect(Sender: TObject; Socket: TCustomWinSocket);
    procedure AcceptThreadFunc;
    procedure ClientThreadFunc(Socket: TCustomWinSocket);
  public
    constructor Create(ANumBuffers, ABufferSize: Integer; const AUserName, APassword: string);
    destructor Destroy; override;
    procedure Start;
    procedure Stop;
  end;
function RIOCreateExtensionFunctionTable(const FunctionTableId: TGUID; const FunctionTable: PRIO_EXTENSION_FUNCTION_TABLE): HRESULT; stdcall; external 'mswsock.dll' name 'RIOCreateExtensionFunctionTable';
implementation

type
  TRIOContext = record
    Socket: TCustomWinSocket;
    Buffer: Pointer;
    Overlapped: TOverlapped;
    ProxyConnected: Boolean;
  end;

  PRIOContext = ^TRIOContext;

const
  MAX_RIO_REQUESTS = 1024;
  MAX_RIO_RESULTS = 1024;
  MAX_RIO_THREADS = 16;
  SOCKS5_VERSION = $05;
  SOCKS5_AUTH_NONE = $00;
  SOCKS5_AUTH_USERNAME_PASSWORD = $02;
  SOCKS5_AUTH_NO_ACCEPTABLE_METHODS = $FF;
  SOCKS5_CMD_CONNECT = $01;
  SOCKS5_ATYP_IPV4 = $01;
  SOCKS5_ATYP_DOMAINNAME = $03;
  SOCKS5_REP_SUCCESS = $00;
  SOCKS5_REP_GENERAL_FAILURE = $01;
  SOCKS5_REP_CONNECTION_NOT_ALLOWED = $02;
  SOCKS5_REP_NETWORK_UNREACHABLE = $03;
  SOCKS5_REP_HOST_UNREACHABLE = $04;
  SOCKS5_REP_CONNECTION_REFUSED = $05;
  SOCKS5_REP_TTL_EXPIRED = $06;
  SOCKS5_REP_COMMAND_NOT_SUPPORTED = $07;
  SOCKS5_REP_ADDRESS_TYPE_NOT_SUPPORTED = $08;

constructor TRioSocks5Server.Create(ANumBuffers, ABufferSize: Integer; const AUserName, APassword: string);
begin
  FNumBuffers := ANumBuffers;
  FBufferSize := ABufferSize;
  FServerSocket := TServerSocket.Create(nil);
  FServerSocket.OnClientConnect := OnClientConnect;
  FServerSocket.OnClientRead := OnClientRead;
  FServerSocket.OnClientDisconnect := OnClientDisconnect;
  FClientThreads := TThreadList.Create;
  FUserName := AUserName;
  FPassword := APassword;
end;

destructor TRioSocks5Server.Destroy;
begin
  Stop;
  FClientThreads.Free;
  FServerSocket.Free;
  inherited;
end;

procedure TRioSocks5Server.Start;
begin
  CreateRIO;
  FServerSocket.Active := True;
  FAcceptThread := TThread.CreateAnonymousThread(AcceptThreadFunc);
  FAcceptThread.FreeOnTerminate := False;
  FAcceptThread.Start;
end;

procedure TRioSocks5Server.Stop;
begin
  FServerSocket.Active := False;
  FAcceptThread.Terminate;
  FAcceptThread.WaitFor;
  DestroyRIO;
end;

procedure TRioSocks5Server.CreateRIO;
var
  ExtensionFunctionTable: RIO_EXTENSION_FUNCTION_TABLE;
  I: Integer;
  Result: HRESULT;
begin
  FillChar(ExtensionFunctionTable, SizeOf(ExtensionFunctionTable), 0);
  ExtensionFunctionTable.cbSize := SizeOf(ExtensionFunctionTable);
  Result := RIOCreateExtensionFunctionTable(@ExtensionFunctionTable, RIO_CORRUPT_CQ or RIO_MSG_DONT_NOTIFY);
  if Result <> S_OK then
    raise Exception.Create('Failed to create RIO extension function table');
  ExtensionFunctionTable.RIOReceive := RIOReceive;
  ExtensionFunctionTable.RIOSend := RIOSend;
  ExtensionFunctionTable.RIOCloseCompletionQueue := RIOCloseCompletionQueue;
  ExtensionFunctionTable.RIOCreateCompletionQueue := RIOCreateCompletionQueue;
  ExtensionFunctionTable.RIOCreateRequestQueue := RIOCreateRequestQueue;
  ExtensionFunctionTable.RIODequeueCompletion := RIODequeueCompletion;
  ExtensionFunctionTable.RIODeregisterBuffer := RIODeregisterBuffer;
  ExtensionFunctionTable.RIORegisterBuffer := RIORegisterBuffer;
  ExtensionFunctionTable.RIONotify := RIONotify;
  ExtensionFunctionTable.RIOReceiveEx := RIOReceiveEx;
  ExtensionFunctionTable.RIOSendEx := RIOSendEx;
  ExtensionFunctionTable.RIOSendMessages := RIOSendMessages;
  ExtensionFunctionTable.RIOReceiveMessages := RIOReceiveMessages;
  ExtensionFunctionTable.RIOResizeCompletionQueue := RIOResizeCompletionQueue;
  ExtensionFunctionTable.RIOResizeRequestQueue := RIOResizeRequestQueue;
  ExtensionFunctionTable.RIOGetCompletionEx := RIOGetCompletionEx;
  ExtensionFunctionTable.RIOGetRequestEx := RIOGetRequestEx;
  ExtensionFunctionTable.RIOGetResultEx := RIOGetResultEx;
  ExtensionFunctionTable.RIODestroyRequestQueue := RIODestroyRequestQueue;
  ExtensionFunctionTable.RIODestroyCompletionQueue := RIODestroyCompletionQueue;
  ExtensionFunctionTable.RIOReceiveMsg := RIOReceiveMsg;
  ExtensionFunctionTable.RIOSendMsg := RIOSendMsg;

  FillChar(FOverlappedArray[0], Length(FOverlappedArray) * SizeOf(TOverlapped), 0);
  FillChar(FBufferArray[0], Length(FBufferArray) * SizeOf(Pointer), 0);

  FRioHandle := CreateEvent(nil, False, False, nil);
  if FRioHandle = 0 then
    raise Exception.Create('Failed to create RIO event');

  Result := RIORegisterBuffer(FBufferArray[0], Length(FBufferArray) * FBufferSize);
  if Result <> S_OK then
    raise Exception.Create('Failed to register RIO buffers');

  ExtensionFunctionTable.MaxOutstandingReceive := FNumBuffers;
  ExtensionFunctionTable.MaxOutstandingSend := FNumBuffers;

  ExtensionFunctionTable.MaxReceiveDataBuffers := 1;
  ExtensionFunctionTable.MaxSendDataBuffers := 1;

  ExtensionFunctionTable.RegistrationGuid := TGUID.Empty;

  ExtensionFunctionTable.ReceiveCompletionRoutine := nil;
  ExtensionFunctionTable.SendCompletionRoutine := nil;

  ExtensionFunctionTable.ReceiveExCancellationRoutine := nil;
  ExtensionFunctionTable.SendExCancellationRoutine := nil;

  ExtensionFunctionTable.GetOverlappedResultEx := nil;

  FRioCompletionPort := ExtensionFunctionTable.RIOCreateCompletionQueue(MAX_RIO_REQUESTS, nil);
  if FRioCompletionPort = RIO_INVALID_CQ then
    raise Exception.Create('Failed to create RIO completion port');

  Result := ExtensionFunctionTable.RIORegisterBuffer(FBufferArray[0], Length(FBufferArray) * FBufferSize);
  if Result <> S_OK then
    raise Exception.Create('Failed to register RIO buffers');

  for I := 0 to MAX_RIO_REQUESTS - 1 do
  begin
    PRIOContext(@FOverlappedArray[I])^.Buffer := Pointer(UIntPtr(FBufferArray[0]) + (I * FBufferSize));
    PRIOContext(@FOverlappedArray[I])^.ProxyConnected := False;
  end;

  Result := ExtensionFunctionTable.RIOCreateRequestQueue(FServerSocket.SocketHandle, MAX_RIO_REQUESTS, 1, MAX_RIO_REQUESTS, FBufferSize, FBufferSize, 0, nil, FRioCompletionPort, nil);
  if Result <> S_OK then
    raise Exception.Create('Failed to create RIO request queue');
end;

procedure TRioSocks5Server.DestroyRIO;
var
  ExtensionFunctionTable: RIO_EXTENSION_FUNCTION_TABLE;
  Result: HRESULT;
begin
  FillChar(ExtensionFunctionTable, SizeOf(ExtensionFunctionTable), 0);
  ExtensionFunctionTable.cbSize := SizeOf(ExtensionFunctionTable);
  Result := RIOCreateExtensionFunctionTable(@ExtensionFunctionTable, RIO_CORRUPT_CQ or RIO_MSG_DONT_NOTIFY);
  if Result <> S_OK then
    raise Exception.Create('Failed to create RIO extension function table');
  Result := ExtensionFunctionTable.RIOCloseCompletionQueue(FRioCompletionPort);
  if Result <> S_OK then
    raise Exception.Create('Failed to close RIO completion port');
  Result := ExtensionFunctionTable.RIODeregisterBuffer(FBufferArray[0]);
  if Result <> S_OK then
    raise Exception.Create('Failed to deregister RIO buffers');
  CloseHandle(FRioHandle);
end;

procedure TRioSocks5Server.OnClientConnect(Sender: TObject; Socket: TCustomWinSocket);
var
  RioRequestContext: PRIOContext;
  BytesReceived: DWORD;
begin
  if Socket.SocketType = stStream then
  begin
    BytesReceived := Socket.ReceiveBuf(FBufferArray[0]^, FBufferSize, 0);
    if BytesReceived >= 3 then
    begin
      if (PByte(FBufferArray[0])^ = SOCKS5_VERSION) and (PByte(FBufferArray[0])^ = BytesReceived - 1) and ((PByte(FBufferArray[0]) + 1)^ > 0) then
      begin
        RioRequestContext := PRIOContext(FOverlappedArray[0].hEvent);
        RioRequestContext.Socket := Socket;
        Move(FBufferArray[0]^, PByte(@RioRequestContext.Buffer)^, BytesReceived);
        RioRequestContext.Buffer := Pointer(UIntPtr(RioRequestContext.Buffer) + BytesReceived);
        RioRequestContext.Overlapped.Offset := 0;
        RioRequestContext.Overlapped.OffsetHigh := 0;
        RioRequestContext.Overlapped.hEvent := FRioHandle;
        RioRequestContext.ProxyConnected := False;
        if (PByte(FBufferArray[0]) + 1)^ >= 1 then
          RioRequestContext.ProxyConnected := True;
        RIOReceive(FServerSocket.SocketHandle, RioRequestContext.Overlapped, @FBufferArray[0]^, 1, 0, RioRequestContext);
        Exit;
      end;
    end;
  end;
  Socket.Close;
end;

procedure TRioSocks5Server.OnClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
  RioRequestContext: PRIOContext;
  BytesReceived: DWORD;
  UserName: string;
  Password: string;
begin
  RioRequestContext := PRIOContext(Socket.Data);
  BytesReceived := 0;
  try
    BytesReceived := Socket.ReceiveBuf(Pointer(UIntPtr(RioRequestContext.Buffer) + RioRequestContext.Overlapped.InternalHigh)^, FBufferSize - RioRequestContext.Overlapped.InternalHigh, 0);
  except
  end;
  if BytesReceived > 0 then
  begin
    if not RioRequestContext.ProxyConnected then
    begin
      if (PByte(RioRequestContext.Buffer)^ = SOCKS5_VERSION) and (PByte(RioRequestContext.Buffer + 1)^ = SOCKS5_AUTH_USERNAME_PASSWORD) and (BytesReceived = 3) then
      begin
        RioRequestContext.Buffer := Pointer(UIntPtr(RioRequestContext.Buffer) + BytesReceived);
        RioRequestContext.Overlapped.Offset := 0;
        RioRequestContext.Overlapped.OffsetHigh := 0;
        RioRequestContext.Overlapped.hEvent := FRioHandle;
        RioRequestContext.ProxyConnected := True;
        RIOReceive(FServerSocket.SocketHandle, RioRequestContext.Overlapped, @FBufferArray[0]^, 1, 0, RioRequestContext);
        Exit;
      end
      else if (PByte(RioRequestContext.Buffer)^ = SOCKS5_VERSION) and (PByte(RioRequestContext.Buffer + 1)^ = SOCKS5_AUTH_NONE) and (BytesReceived = 2) then
      begin
        RioRequestContext.Buffer := Pointer(UIntPtr(RioRequestContext.Buffer) + BytesReceived);
        RioRequestContext.Overlapped.Offset := 0;
        RioRequestContext.Overlapped.OffsetHigh := 0;
        RioRequestContext.Overlapped.hEvent := FRioHandle;
        RioRequestContext.ProxyConnected := True;
        RIOReceive(FServerSocket.SocketHandle, RioRequestContext.Overlapped, @FBufferArray[0]^, 1, 0, RioRequestContext);
        Exit;
      end
      else
      begin
        Socket.Close;
        Exit;
      end;
    end
    else if (PByte(RioRequestContext.Buffer)^ = SOCKS5_VERSION) and (PByte(RioRequestContext.Buffer + 1)^ = SOCKS5_CMD_CONNECT) and (PByte(RioRequestContext.Buffer + 2)^ = 0) then
    begin
      case PByte(RioRequestContext.Buffer + 3)^ of
        SOCKS5_ATYP_IPV4:
          begin
            if BytesReceived >= 10 then
            begin
              RioRequestContext.Buffer := Pointer(UIntPtr(RioRequestContext.Buffer) + BytesReceived);
              RioRequestContext.Overlapped.Offset := 0;
              RioRequestContext.Overlapped.OffsetHigh := 0;
              RioRequestContext.Overlapped.hEvent := FRioHandle;
              RIOReceive(FServerSocket.SocketHandle, RioRequestContext.Overlapped, @FBufferArray[0]^, 1, 0, RioRequestContext);
              Exit;
            end;
          end;
        SOCKS5_ATYP_DOMAINNAME:
          begin
            if (BytesReceived >= 5) and (BytesReceived = 5 + PByte(RioRequestContext.Buffer + 4)^) then
            begin
              RioRequestContext.Buffer := Pointer(UIntPtr(RioRequestContext.Buffer) + BytesReceived);
              RioRequestContext.Overlapped.Offset := 0;
              RioRequestContext.Overlapped.OffsetHigh := 0;
              RioRequestContext.Overlapped.hEvent := FRioHandle;
              RIOReceive(FServerSocket.SocketHandle, RioRequestContext.Overlapped, @FBufferArray[0]^, 1, 0, RioRequestContext);
              Exit;
            end;
          end;
        SOCKS5_ATYP_IPV6:
          begin
            if BytesReceived >= 22 then
            begin
              RioRequestContext.Buffer := Pointer(UIntPtr(RioRequestContext.Buffer) + BytesReceived);
              RioRequestContext.Overlapped.Offset := 0;
              RioRequestContext.Overlapped.OffsetHigh := 0;
              RioRequestContext.Overlapped.hEvent := FRioHandle;
              RIOReceive(FServerSocket.SocketHandle, RioRequestContext.Overlapped, @FBufferArray[0]^, 1, 0, RioRequestContext);
              Exit;
            end;
          end;
      end;
      Socket.Close;
      Exit;
    end
    else if (PByte(RioRequestContext.Buffer)^ = SOCKS5_VERSION) and (PByte(RioRequestContext.Buffer + 1)^ = SOCKS5_CMD_CONNECT) and (PByte(RioRequestContext.Buffer + 2)^ = $FF) then
    begin
      Socket.Close;
      Exit;
    end
    else if (PByte(RioRequestContext.Buffer)^ = SOCKS5_VERSION) and (PByte(RioRequestContext.Buffer + 1)^ = SOCKS5_CMD_CONNECT) and (PByte(RioRequestContext.Buffer + 2)^ = $02) and (BytesReceived >= 3) then
    begin
      if (PByte(RioRequestContext.Buffer + 3)^ = 0) and (BytesReceived = 4) then
      begin
        RioRequestContext.Buffer := Pointer(UIntPtr(RioRequestContext.Buffer) + BytesReceived);
        RioRequestContext.Overlapped.Offset := 0;
        RioRequestContext.Overlapped.OffsetHigh := 0;
        RioRequestContext.Overlapped.hEvent := FRioHandle;
        RIOReceive(FServerSocket.SocketHandle, RioRequestContext.Overlapped, @FBufferArray[0]^, 1, 0, RioRequestContext);
        Exit;
      end
      else if (PByte(RioRequestContext.Buffer + 3)^ = 1) and (BytesReceived >= 5) and (PByte(RioRequestContext.Buffer + 4)^ > 0) then
      begin
        SetLength(UserName, PByte(RioRequestContext.Buffer + 4)^);
        Move((PByte(RioRequestContext.Buffer + 5))^, PByte(UserName)^, Length(UserName));
        RioRequestContext.Buffer := Pointer(UIntPtr(RioRequestContext.Buffer) + BytesReceived);
        RioRequestContext.Overlapped.Offset := 0;
        RioRequestContext.Overlapped.OffsetHigh := 0;
        RioRequestContext.Overlapped.hEvent := FRioHandle;
        RIOReceive(FServerSocket.SocketHandle, RioRequestContext.Overlapped, @FBufferArray[0]^, 1, 0, RioRequestContext);
        Exit;
      end
      else if (PByte(RioRequestContext.Buffer + 3)^ = 2) and (BytesReceived >= 6) and (PByte(RioRequestContext.Buffer + 4)^ > 0) then
      begin
        SetLength(UserName, PByte(RioRequestContext.Buffer + 4)^);
        Move((PByte(RioRequestContext.Buffer + 5))^, PByte(UserName)^, Length(UserName));
        if (BytesReceived >= 6 + Length(UserName)) and (PByte(RioRequestContext.Buffer + 5 + Length(UserName))^ > 0) then
        begin
          SetLength(Password, PByte(RioRequestContext.Buffer + 5 + Length(UserName))^);
          Move((PByte(RioRequestContext.Buffer + 6 + Length(UserName)))^, PByte(Password)^, Length(Password));
          if (not FAuthRequired) or ((FAuthRequired) and (FAuthenticator.Authenticate(UserName, Password))) then
          begin
            RioRequestContext.Buffer := Pointer(UIntPtr(RioRequestContext.Buffer) + BytesReceived);
            RioRequestContext.Overlapped.Offset := 0;
            RioRequestContext.Overlapped.OffsetHigh := 0;
            RioRequestContext.Overlapped.hEvent := FRioHandle;
            RIOReceive(FServerSocket.SocketHandle, RioRequestContext.Overlapped, @FBufferArray[0]^, 1, 0, RioRequestContext);
            Exit;
          end
          else
          begin
            FServerSocket.SocketConnection[Socket.Handle].SendBuf(SOCKS5_AUTH_FAILED_RESPONSE, 2);
            Socket.Close;
            Exit;
          end;
        end;
      end;
      Socket.Close;
      Exit;
    end
    else
    begin
      RioRequestContext.Socket.SocketType := stCustom;
      FProxyServer.BeginProxyRequest(RioRequestContext.Socket, FBufferArray[0]^, BytesReceived);
      RioRequestContext.Socket := nil;
      RioRequestContext.ProxyConnected := False;
      RioRequestContext.Buffer := Pointer(UIntPtr(RioRequestContext.Buffer) + BytesReceived);
      RioRequestContext.Overlapped.Offset := 0;
      RioRequestContext.Overlapped.OffsetHigh := 0;
      RioRequestContext.Overlapped.hEvent := FRioHandle;
      RIOReceive(FServerSocket.SocketHandle, RioRequestContext.Overlapped, @FBufferArray[0]^, 1, 0, RioRequestContext);
      Exit;
    end;
  end;
  Socket.Close;
end;

procedure TRioSocks5Server.OnProxyComplete(Sender: TObject; ClientSocket, ProxySocket: TCustomWinSocket; Request: TProxyRequest; Buffer: TBytes; BytesTransferred: Integer);
var
  RioRequestContext: PRIOContext;
  SentBytes: Integer;
begin
  if not Assigned(ClientSocket) or not Assigned(ProxySocket) then
    Exit;
  if not (ClientSocket.SocketType = stCustom) or not (ProxySocket.SocketType = stCustom) then
    Exit;
  RioRequestContext := PRIOContext(ClientSocket.Data);
  if not Assigned(RioRequestContext) then
  begin
    ClientSocket.Close;
    ProxySocket.Close;
    Exit;
  end;
  if not RioRequestContext.ProxyConnected then
  begin
    ClientSocket.Close;
    ProxySocket.Close;
    Exit;
  end;
  try
    ProxySocket.SendBuf(Buffer[0], BytesTransferred);
    RioRequestContext.Socket := ProxySocket;
    RioRequestContext.Overlapped.Offset := 0;
    RioRequestContext.Overlapped.OffsetHigh := 0;
    RioRequestContext.Overlapped.hEvent := FRioHandle;
    RIOReceive(FServerSocket.SocketHandle, RioRequestContext.Overlapped, @FBufferArray[0]^, 1, 0, RioRequestContext);
  except
    ClientSocket.Close;
    ProxySocket.Close;
  end;
end;

end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值