Статья VNC через Relay (Работа за NAT) или пробуем повторить AnyDesk на Паскале.[2]

Admin

Администратор

VNC через Relay (Работа за NAT) или пробуем повторить AnyDesk на Паскале. [2]​


Исходник релея:

Код:
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls,
  Windows, WinSock2, LMessages, SyncObjs, Contnrs;

const
  RELAY_PORT = 5500;
  MAX_CONNECTIONS = 100;
  ID_LENGTH = 9;
  WM_SOCKET = WM_USER + 1;

type
  TConnectionType = (ctUnknown, ctVncServer, ctVncClient);
  TPeerState = (psWaiting, psConnected, psDisconnected);

  TScreenHeader = packed record
    Width: DWORD;
    Height: DWORD;
    DataSize: DWORD;
    ImageFormat: Byte;  // 0 = RAW, 1 = JPEG, 2 = PNG
  end;

  TConnectionInfo = record
    Socket: TSocket;
    ConnectionType: TConnectionType;
    PeerID: array[0..ID_LENGTH-1] of Char;
    ConnectedTo: array[0..ID_LENGTH-1] of Char;
    RemoteIP: string;
    RemotePort: Integer;
    LastActive: TDateTime;
    PeerState: TPeerState;
    DataBuffer: TMemoryStream;
    CriticalSection: TCriticalSection;
    ExpectingHeader: Boolean;
    CurrentHeader: TScreenHeader;
    ImageDataReceived: Integer;
  end;
  PConnectionInfo = ^TConnectionInfo;

  TPeerConnection = record
    ID: array[0..ID_LENGTH-1] of Char;
    ServerConnection: TSocket;
    ClientConnection: TSocket;
    State: TPeerState;
    Created: TDateTime;
  end;
  PPeerConnection = ^TPeerConnection;

  { TRelayServerForm }

  TRelayServerForm = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Edit1: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Memo1: TMemo;
    Panel1: TPanel;
    Timer1: TTimer;

    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);

  private
    FServerSocket: TSocket;
    FWSAData: TWSADATA;
    FRunning: Boolean;
    FConnections: array[0..MAX_CONNECTIONS-1] of TConnectionInfo;
    FPeerConnections: TList;
    FCriticalSection: TCriticalSection;

    function GenerateID: string;
    function FindFreeConnectionSlot: Integer;
    function FindConnectionBySocket(Socket: TSocket): PConnectionInfo;
    function FindConnectionByID(const ID: string): PConnectionInfo;
    function FindPeerByID(const ID: string): PPeerConnection;
    function CreatePeer(const ID: string; ServerSocket: TSocket): PPeerConnection;
    procedure RemovePeer(Peer: PPeerConnection);
    procedure AcceptConnection;
    procedure ProcessConnection(Socket: TSocket);
    procedure HandleRegistration(Socket: TSocket; const Data: string);
    procedure HandleConnectRequest(Socket: TSocket; const Data: string);
    procedure RelayData(SourceSocket: TSocket; const Data: array of Byte; DataSize: Integer);
    procedure RelayDataStr(SourceSocket: TSocket; const Data: string);
    procedure DisconnectConnection(Socket: TSocket);
    procedure StartServer;
    procedure StopServer;
    procedure UpdateConnectionsCount;
    procedure SendResponse(Socket: TSocket; const Response: string);
    procedure AddLog(const Msg: string);
    procedure ClearLog(Sender: TObject);



  protected
    procedure WMSocket(var Message: TLMessage); message WM_SOCKET;

  public

  end;

var
  RelayServerForm: TRelayServerForm;

implementation

{$R *.lfm}

function RandomString(Length: Integer): string;
const
  Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
var
  i, CharCount: Integer;
begin
  Result := '';
  CharCount := System.Length(Chars);
  for i := 1 to Length do
    Result := Result + Chars[Random(CharCount) + 1];
end;

{ TRelayServerForm }

procedure TRelayServerForm.FormCreate(Sender: TObject);
var
  i: Integer;
begin
  Caption := 'VNC Relay Server (JPEG Support)';
  Button1.Caption := 'Start';
  Button2.Caption := 'Stop';
  Button2.Enabled := False;
  Button3.Caption := 'Clear Log';
  Edit1.Text := IntToStr(RELAY_PORT);
  Label1.Caption := 'Stopped';
  Label2.Caption := 'Connections: 0';
  Label3.Caption := 'Peers: 0';
  Label4.Caption := 'Port:';
  Label5.Caption := 'Server';

  FServerSocket := INVALID_SOCKET;
  FRunning := False;

  Randomize;

  for i := 0 to MAX_CONNECTIONS - 1 do
  begin
    FConnections[i].Socket := INVALID_SOCKET;
    FConnections[i].ConnectionType := ctUnknown;
    FillChar(FConnections[i].PeerID, ID_LENGTH, 0);
    FillChar(FConnections[i].ConnectedTo, ID_LENGTH, 0);
    FConnections[i].RemoteIP := '';
    FConnections[i].RemotePort := 0;
    FConnections[i].LastActive := Now;
    FConnections[i].PeerState := psDisconnected;
    FConnections[i].DataBuffer := TMemoryStream.Create;
    FConnections[i].CriticalSection := TCriticalSection.Create;
    FConnections[i].ExpectingHeader := True;
    FConnections[i].ImageDataReceived := 0;
  end;

  FPeerConnections := TList.Create;
  FCriticalSection := TCriticalSection.Create;

  if WSAStartup(MAKEWORD(2, 2), FWSAData) <> 0 then
  begin
    ShowMessage('Winsock init failed');
    Exit;
  end;

  Timer1.Interval := 10000;
  Timer1.Enabled := False;

  AddLog('JPEG Relay Server ready');
end;

procedure TRelayServerForm.FormDestroy(Sender: TObject);
var
  i: Integer;
  Peer: PPeerConnection;
begin
  StopServer;

  for i := 0 to MAX_CONNECTIONS - 1 do
  begin
    if FConnections[i].DataBuffer <> nil then
      FreeAndNil(FConnections[i].DataBuffer);
    if FConnections[i].CriticalSection <> nil then
      FreeAndNil(FConnections[i].CriticalSection);
  end;

  FCriticalSection.Enter;
  try
    for i := 0 to FPeerConnections.Count - 1 do
    begin
      Peer := PPeerConnection(FPeerConnections[i]);
      Dispose(Peer);
    end;
    FreeAndNil(FPeerConnections);
  finally
    FCriticalSection.Leave;
    FreeAndNil(FCriticalSection);
  end;

  WSACleanup;
end;

function TRelayServerForm.GenerateID: string;
begin
  Result := RandomString(8);
  AddLog('Generated new ID: ' + Result);
end;

function TRelayServerForm.FindFreeConnectionSlot: Integer;
var
  i: Integer;
begin
  for i := 0 to MAX_CONNECTIONS - 1 do
  begin
    if FConnections[i].Socket = INVALID_SOCKET then
    begin
      Result := i;
      Exit;
    end;
  end;
  Result := -1;
end;

function TRelayServerForm.FindConnectionBySocket(Socket: TSocket): PConnectionInfo;
var
  i: Integer;
begin
  for i := 0 to MAX_CONNECTIONS - 1 do
  begin
    if FConnections[i].Socket = Socket then
    begin
      Result := @FConnections[i];
      Exit;
    end;
  end;
  Result := nil;
end;

function TRelayServerForm.FindConnectionByID(const ID: string): PConnectionInfo;
var
  i: Integer;
begin
  for i := 0 to MAX_CONNECTIONS - 1 do
  begin
    if (FConnections[i].Socket <> INVALID_SOCKET) and
       (StrPas(FConnections[i].PeerID) = ID) then
    begin
      Result := @FConnections[i];
      Exit;
    end;
  end;
  Result := nil;
end;

function TRelayServerForm.FindPeerByID(const ID: string): PPeerConnection;
var
  i: Integer;
  Peer: PPeerConnection;
begin
  Result := nil;
  FCriticalSection.Enter;
  try
    for i := 0 to FPeerConnections.Count - 1 do
    begin
      Peer := PPeerConnection(FPeerConnections[i]);
      if StrPas(Peer^.ID) = ID then
      begin
        Result := Peer;
        Exit;
      end;
    end;
  finally
    FCriticalSection.Leave;
  end;
end;

function TRelayServerForm.CreatePeer(const ID: string; ServerSocket: TSocket): PPeerConnection;
begin
  New(Result);
  FillChar(Result^, SizeOf(TPeerConnection), 0);
  StrPCopy(Result^.ID, ID);
  Result^.ServerConnection := ServerSocket;
  Result^.ClientConnection := INVALID_SOCKET;
  Result^.State := psWaiting;
  Result^.Created := Now;

  FCriticalSection.Enter;
  try
    FPeerConnections.Add(Result);
    Label3.Caption := 'Peers: ' + IntToStr(FPeerConnections.Count);
  finally
    FCriticalSection.Leave;
  end;

  AddLog('Created peer for ID: ' + ID);
end;

procedure TRelayServerForm.RemovePeer(Peer: PPeerConnection);
begin
  FCriticalSection.Enter;
  try
    FPeerConnections.Remove(Peer);
    Dispose(Peer);
    Label3.Caption := 'Peers: ' + IntToStr(FPeerConnections.Count);
  finally
    FCriticalSection.Leave;
  end;
end;

procedure TRelayServerForm.Button1Click(Sender: TObject);
begin
  StartServer;
end;

procedure TRelayServerForm.Button2Click(Sender: TObject);
begin
  StopServer;
end;

procedure TRelayServerForm.Button3Click(Sender: TObject);
begin
  Memo1.Lines.Clear;
  AddLog('Log cleared');
end;

procedure TRelayServerForm.ClearLog(Sender: TObject);
begin
  Memo1.Lines.Clear;
  AddLog('Log cleared');
end;

procedure TRelayServerForm.Timer1Timer(Sender: TObject);
var
  i: Integer;
  Peer: PPeerConnection;
  j: Integer;
begin
  for i := 0 to MAX_CONNECTIONS - 1 do
  begin
    if FConnections[i].Socket <> INVALID_SOCKET then
    begin
      if (Now - FConnections[i].LastActive) > (5 / 1440) then
      begin
        AddLog('Disconnecting inactive connection: ' +
               StrPas(FConnections[i].PeerID));
        DisconnectConnection(FConnections[i].Socket);
      end;
    end;
  end;

  FCriticalSection.Enter;
  try
    for j := FPeerConnections.Count - 1 downto 0 do
    begin
      Peer := PPeerConnection(FPeerConnections[j]);
      if (Peer^.State = psWaiting) and
         ((Now - Peer^.Created) > (2 / 1440)) then
      begin
        AddLog('Removing expired peer: ' + StrPas(Peer^.ID));
        FPeerConnections.Delete(j);
        Dispose(Peer);
      end;
    end;
    Label3.Caption := 'Peers: ' + IntToStr(FPeerConnections.Count);
  finally
    FCriticalSection.Leave;
  end;
end;

procedure TRelayServerForm.StartServer;
var
  ServerAddr: TSockAddrIn;
  Port: Integer;
begin
  if FRunning then Exit;

  Port := StrToIntDef(Edit1.Text, RELAY_PORT);

  try
    FServerSocket := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if FServerSocket = INVALID_SOCKET then
      raise Exception.Create('Socket creation failed');

    FillChar(ServerAddr, SizeOf(ServerAddr), 0);
    ServerAddr.sin_family := AF_INET;
    ServerAddr.sin_addr.s_addr := INADDR_ANY;
    ServerAddr.sin_port := htons(Port);

    if bind(FServerSocket, @ServerAddr, SizeOf(ServerAddr)) = SOCKET_ERROR then
      raise Exception.Create('Bind failed on port ' + IntToStr(Port));

    if listen(FServerSocket, SOMAXCONN) = SOCKET_ERROR then
      raise Exception.Create('Listen failed');

    if WSAAsyncSelect(FServerSocket, Handle, WM_SOCKET, FD_ACCEPT) = SOCKET_ERROR then
      raise Exception.Create('WSAAsyncSelect failed');

    FRunning := True;
    Button1.Enabled := False;
    Button2.Enabled := True;
    Timer1.Enabled := True;

    Label1.Caption := 'Running on port ' + IntToStr(Port);
    AddLog('JPEG Relay Server started on port ' + IntToStr(Port));

  except
    on E: Exception do
    begin
      AddLog('Start error: ' + E.Message);
      ShowMessage('Start error: ' + E.Message);
      if FServerSocket <> INVALID_SOCKET then
      begin
        closesocket(FServerSocket);
        FServerSocket := INVALID_SOCKET;
      end;
    end;
  end;
end;

procedure TRelayServerForm.StopServer;
var
  i: Integer;
  Peer: PPeerConnection;
  j: Integer;
begin
  if not FRunning then Exit;

  FRunning := False;
  Timer1.Enabled := False;

  for i := 0 to MAX_CONNECTIONS - 1 do
  begin
    if FConnections[i].Socket <> INVALID_SOCKET then
      DisconnectConnection(FConnections[i].Socket);
  end;

  FCriticalSection.Enter;
  try
    for j := 0 to FPeerConnections.Count - 1 do
    begin
      Peer := PPeerConnection(FPeerConnections[j]);
      Dispose(Peer);
    end;
    FPeerConnections.Clear;
    Label3.Caption := 'Peers: 0';
  finally
    FCriticalSection.Leave;
  end;

  if FServerSocket <> INVALID_SOCKET then
  begin
    closesocket(FServerSocket);
    FServerSocket := INVALID_SOCKET;
  end;

  Button1.Enabled := True;
  Button2.Enabled := False;
  Label1.Caption := 'Stopped';
  Label2.Caption := 'Connections: 0';
  AddLog('Relay Server stopped');
end;

procedure TRelayServerForm.AcceptConnection;
var
  ClientSocket: TSocket;
  ClientAddr: TSockAddrIn;
  AddrLen: Integer;
  Slot: Integer;
  Conn: PConnectionInfo;
  BufSize: Integer;
begin
  AddrLen := SizeOf(ClientAddr);
  ClientSocket := accept(FServerSocket, @ClientAddr, @AddrLen);

  if ClientSocket = INVALID_SOCKET then
  begin
    AddLog('Accept failed: ' + IntToStr(WSAGetLastError));
    Exit;
  end;

  BufSize := 1024 * 1024;
  setsockopt(ClientSocket, SOL_SOCKET, SO_RCVBUF, @BufSize, SizeOf(BufSize));
  setsockopt(ClientSocket, SOL_SOCKET, SO_SNDBUF, @BufSize, SizeOf(BufSize));

  Slot := FindFreeConnectionSlot;
  if Slot = -1 then
  begin
    AddLog('No free slots, rejecting connection');
    closesocket(ClientSocket);
    Exit;
  end;

  Conn := @FConnections[Slot];
  Conn^.Socket := ClientSocket;
  Conn^.ConnectionType := ctUnknown;
  FillChar(Conn^.PeerID, ID_LENGTH, 0);
  FillChar(Conn^.ConnectedTo, ID_LENGTH, 0);
  Conn^.RemoteIP := string(inet_ntoa(ClientAddr.sin_addr));
  Conn^.RemotePort := ntohs(ClientAddr.sin_port);
  Conn^.LastActive := Now;
  Conn^.PeerState := psDisconnected;
  Conn^.DataBuffer.Clear;
  Conn^.ExpectingHeader := True;
  Conn^.ImageDataReceived := 0;

  WSAAsyncSelect(ClientSocket, Self.Handle, WM_SOCKET, FD_READ or FD_CLOSE);

  UpdateConnectionsCount;

  AddLog('New connection from ' + Conn^.RemoteIP + ':' + IntToStr(Conn^.RemotePort) +
         ', socket: ' + IntToStr(ClientSocket));
end;


procedure TRelayServerForm.ProcessConnection(Socket: TSocket);
var
  Buffer: array[0..16383] of Byte;
  BytesReceived: Integer;
  DataStr: string;
  Lines: TStringList;
  i: Integer;
  Line: string;
  Conn: PConnectionInfo;
  DataStart: Integer;
  TargetSocket: TSocket;
  Peer: PPeerConnection;
  BytesSent: Integer;
begin
  Conn := FindConnectionBySocket(Socket);
  if Conn = nil then Exit;

  BytesReceived := recv(Socket, Buffer, SizeOf(Buffer), 0);

  if BytesReceived <= 0 then
  begin
    DisconnectConnection(Socket);
    Exit;
  end;

  Conn^.LastActive := Now;

  Conn^.CriticalSection.Enter;
  try
    if Conn^.PeerState = psConnected then
    begin
      TargetSocket := INVALID_SOCKET;

      if Conn^.ConnectionType = ctVncServer then
      begin
        Peer := FindPeerByID(StrPas(Conn^.PeerID));
        if (Peer <> nil) and (Peer^.State = psConnected) then
          TargetSocket := Peer^.ClientConnection;
      end
      else if Conn^.ConnectionType = ctVncClient then
      begin
        Peer := FindPeerByID(StrPas(Conn^.ConnectedTo));
        if (Peer <> nil) and (Peer^.State = psConnected) then
          TargetSocket := Peer^.ServerConnection;
      end;

      if TargetSocket <> INVALID_SOCKET then
      begin
        BytesSent := send(TargetSocket, Buffer, BytesReceived, 0);
        if BytesSent = SOCKET_ERROR then
        begin
          AddLog('Relay send error: ' + IntToStr(WSAGetLastError));
          DisconnectConnection(Socket);
          DisconnectConnection(TargetSocket);
        end
        else if BytesSent < BytesReceived then
        begin
          AddLog('Warning: Partial send: ' + IntToStr(BytesSent) + '/' + IntToStr(BytesReceived));
        end
        else
        begin
          AddLog(Format('Relayed %d bytes directly', [BytesSent]));
        end;
      end
      else
      begin
        AddLog('Error: No target socket for connected peer');
      end;
    end
    else
    begin
      Conn^.DataBuffer.Write(Buffer, BytesReceived);
      Conn^.DataBuffer.Position := 0;

      SetLength(DataStr, Conn^.DataBuffer.Size);
      Conn^.DataBuffer.Read(DataStr[1], Conn^.DataBuffer.Size);

      DataStart := Pos(#13#10, DataStr);

      if DataStart > 0 then
      begin
        Lines := TStringList.Create;
        try
          Lines.Text := Copy(DataStr, 1, DataStart + 1);

          for i := 0 to Lines.Count - 1 do
          begin
            Line := Trim(Lines[i]);
            if Line = '' then Continue;

            AddLog('Received: ' + Line);

            if Pos('REGISTER_SERVER', Line) = 1 then
            begin
              HandleRegistration(Socket, Line);
            end
            else if Pos('CONNECT_CLIENT', Line) = 1 then
            begin
              HandleConnectRequest(Socket, Line);
            end;
          end;
        finally
          Lines.Free;
        end;

        if DataStart + 2 <= Length(DataStr) then
        begin
          Conn^.DataBuffer.Clear;
          Conn^.DataBuffer.Write(DataStr[DataStart + 2], Length(DataStr) - DataStart - 1);
        end
        else
        begin
          Conn^.DataBuffer.Clear;
        end;
      end;
    end;
  finally
    Conn^.CriticalSection.Leave;
  end;
end;

procedure TRelayServerForm.HandleRegistration(Socket: TSocket; const Data: string);
var
  Conn: PConnectionInfo;
  ID: string;
  Peer: PPeerConnection;
  CmdParts: TStringList;
begin
  Conn := FindConnectionBySocket(Socket);
  if Conn = nil then Exit;

  CmdParts := TStringList.Create;
  try
    CmdParts.Delimiter := ' ';
    CmdParts.DelimitedText := Data;

    if CmdParts.Count >= 2 then
    begin
      ID := CmdParts[1];
    end
    else
    begin
      ID := GenerateID;
    end;

    if FindConnectionByID(ID) <> nil then
    begin
      AddLog('ID ' + ID + ' already registered, generating new one');
      ID := GenerateID;
    end;

    StrPCopy(Conn^.PeerID, ID);
    Conn^.ConnectionType := ctVncServer;

    Peer := CreatePeer(ID, Socket);

    SendResponse(Socket, 'REGISTERED ' + ID);

    AddLog('Server registered with ID: ' + ID);
  finally
    CmdParts.Free;
  end;
end;

procedure TRelayServerForm.HandleConnectRequest(Socket: TSocket; const Data: string);
var
  Conn: PConnectionInfo;
  TargetID: string;
  Peer: PPeerConnection;
  ServerConn: PConnectionInfo;
  CmdParts: TStringList;
begin
  Conn := FindConnectionBySocket(Socket);
  if Conn = nil then
  begin
    AddLog('HandleConnectRequest: Connection not found for socket ' + IntToStr(Socket));
    Exit;
  end;

  CmdParts := TStringList.Create;
  try
    CmdParts.Delimiter := ' ';
    CmdParts.DelimitedText := Data;

    if CmdParts.Count >= 2 then
    begin
      TargetID := CmdParts[1];
      AddLog('Looking for server with ID: ' + TargetID);

      Peer := FindPeerByID(TargetID);

      if (Peer <> nil) and (Peer^.State = psWaiting) then
      begin
        AddLog('Found waiting server: ' + TargetID);

        Peer^.ClientConnection := Socket;
        Peer^.State := psConnected;

        StrPCopy(Conn^.PeerID, 'CLIENT_' + TargetID);
        StrPCopy(Conn^.ConnectedTo, TargetID);
        Conn^.ConnectionType := ctVncClient;
        Conn^.PeerState := psConnected;

        ServerConn := FindConnectionBySocket(Peer^.ServerConnection);
        if ServerConn <> nil then
        begin
          StrPCopy(ServerConn^.ConnectedTo, 'CLIENT_' + TargetID);
          ServerConn^.PeerState := psConnected;

          ServerConn^.ExpectingHeader := True;
          ServerConn^.ImageDataReceived := 0;

          SendResponse(Peer^.ServerConnection, 'CLIENT_CONNECTED');
          AddLog('Sent CLIENT_CONNECTED to server');
        end
        else
        begin
          AddLog('Warning: Server connection not found');
        end;

        SendResponse(Socket, 'CONNECTED');
        AddLog('Sent CONNECTED to client');

        AddLog('Client connected to server ' + TargetID);
      end
      else if Peer = nil then
      begin
        SendResponse(Socket, 'ERROR Server not found: ' + TargetID);
        AddLog('Client requested connection to unknown ID: ' + TargetID);
      end
      else
      begin
        SendResponse(Socket, 'ERROR Server busy');
        AddLog('Server ' + TargetID + ' is busy');
      end;
    end
    else
    begin
      SendResponse(Socket, 'ERROR Invalid command format');
      AddLog('Invalid CONNECT_CLIENT command: ' + Data);
    end;
  finally
    CmdParts.Free;
  end;
end;

procedure TRelayServerForm.RelayData(SourceSocket: TSocket; const Data: array of Byte; DataSize: Integer);
begin
  RelayDataStr(SourceSocket, string(AnsiString(PAnsiChar(@Data[0]))));
end;

procedure TRelayServerForm.RelayDataStr(SourceSocket: TSocket; const Data: string);
var
  SourceConn, TargetConn: PConnectionInfo;
  TargetSocket: TSocket;
  BytesSent: Integer;
  Peer: PPeerConnection;
  DataToSend: AnsiString;
begin
  SourceConn := FindConnectionBySocket(SourceSocket);
  if SourceConn = nil then Exit;

  TargetSocket := INVALID_SOCKET;

  if SourceConn^.ConnectionType = ctVncServer then
  begin
    Peer := FindPeerByID(StrPas(SourceConn^.PeerID));
    if (Peer <> nil) and (Peer^.State = psConnected) then
      TargetSocket := Peer^.ClientConnection;
  end
  else if SourceConn^.ConnectionType = ctVncClient then
  begin
    Peer := FindPeerByID(StrPas(SourceConn^.ConnectedTo));
    if (Peer <> nil) and (Peer^.State = psConnected) then
      TargetSocket := Peer^.ServerConnection;
  end;

  if TargetSocket <> INVALID_SOCKET then
  begin
    DataToSend := AnsiString(Data);
    BytesSent := send(TargetSocket, DataToSend[1], Length(DataToSend), 0);
    if BytesSent = SOCKET_ERROR then
    begin
      AddLog('Relay error: ' + IntToStr(WSAGetLastError));
      DisconnectConnection(SourceSocket);
      DisconnectConnection(TargetSocket);
    end
    else
    begin
      AddLog(Format('Relayed %d bytes to target', [BytesSent]));
    end;
  end;
end;

procedure TRelayServerForm.DisconnectConnection(Socket: TSocket);
var
  Conn: PConnectionInfo;
  Peer: PPeerConnection;
begin
  Conn := FindConnectionBySocket(Socket);
  if Conn = nil then Exit;

  AddLog('Disconnecting: ' + StrPas(Conn^.PeerID) +
         ' (' + Conn^.RemoteIP + ':' + IntToStr(Conn^.RemotePort) + ')');

  if Conn^.ConnectionType = ctVncServer then
  begin
    Peer := FindPeerByID(StrPas(Conn^.PeerID));
    if Peer <> nil then
    begin
      if Peer^.ClientConnection <> INVALID_SOCKET then
        SendResponse(Peer^.ClientConnection, 'SERVER_DISCONNECTED');
      RemovePeer(Peer);
    end;
  end
  else if Conn^.ConnectionType = ctVncClient then
  begin
    Peer := FindPeerByID(StrPas(Conn^.ConnectedTo));
    if (Peer <> nil) and (Peer^.State = psConnected) then
    begin
      if Peer^.ServerConnection <> INVALID_SOCKET then
        SendResponse(Peer^.ServerConnection, 'CLIENT_DISCONNECTED');
      Peer^.ClientConnection := INVALID_SOCKET;
      Peer^.State := psWaiting;
      Peer^.Created := Now;
    end;
  end;

  WSAAsyncSelect(Socket, Handle, 0, 0);
  closesocket(Socket);

  Conn^.Socket := INVALID_SOCKET;
  Conn^.ConnectionType := ctUnknown;
  FillChar(Conn^.PeerID, ID_LENGTH, 0);
  FillChar(Conn^.ConnectedTo, ID_LENGTH, 0);
  Conn^.RemoteIP := '';
  Conn^.RemotePort := 0;
  Conn^.PeerState := psDisconnected;
  Conn^.DataBuffer.Clear;
  Conn^.ExpectingHeader := True;
  Conn^.ImageDataReceived := 0;

  UpdateConnectionsCount;
end;

procedure TRelayServerForm.UpdateConnectionsCount;
var
  i, Count: Integer;
begin
  Count := 0;
  for i := 0 to MAX_CONNECTIONS - 1 do
  begin
    if FConnections[i].Socket <> INVALID_SOCKET then
      Inc(Count);
  end;
  Label2.Caption := 'Connections: ' + IntToStr(Count);
end;

procedure TRelayServerForm.SendResponse(Socket: TSocket; const Response: string);
var
  FullResponse: AnsiString;
  BytesSent: Integer;
begin
  FullResponse := AnsiString(Response + #13#10);
  BytesSent := send(Socket, FullResponse[1], Length(FullResponse), 0);
  if BytesSent = SOCKET_ERROR then
  begin
    AddLog('Send error: ' + IntToStr(WSAGetLastError));
  end;
end;

procedure TRelayServerForm.AddLog(const Msg: string);
begin
  if Memo1.Lines.Count > 1000 then
    Memo1.Lines.Clear;

  Memo1.Lines.Add('[' + TimeToStr(Now) + '] ' + Msg);
  Memo1.SelStart := Length(Memo1.Text);
end;

procedure TRelayServerForm.WMSocket(var Message: TLMessage);
var
  Event: Word;
  Error: Integer;
  Socket: TSocket;
begin
  Event := LOWORD(Message.LParam);
  Error := HIWORD(Message.LParam);
  Socket := Message.WParam;

  try
    case Event of
      FD_ACCEPT:
        AcceptConnection;

      FD_READ:
        begin
          if Error = 0 then
          begin
            if Socket = FServerSocket then
            begin
              // Server socket - ignore
            end
            else
            begin
              ProcessConnection(Socket);
            end;
          end
          else
          begin
            AddLog('FD_READ error: ' + IntToStr(Error));
            DisconnectConnection(Socket);
          end;
        end;

      FD_CLOSE:
        begin
          AddLog('FD_CLOSE event for socket ' + IntToStr(Socket));
          DisconnectConnection(Socket);
        end;

      else
        AddLog('Unknown socket event: ' + IntToStr(Event));
    end;

  except
    on E: Exception do
    begin
      AddLog('Critical error in WMSocket: ' + E.Message);
      if Socket <> FServerSocket then
        DisconnectConnection(Socket);
    end;
  end;
end;

end.
 
Похожие темы
Admin Статья VNC через Relay (Работа за NAT) или пробуем повторить AnyDesk на Паскале.[3] Анонимность и приватность 0
Admin Статья VNC через Relay (Работа за NAT) или пробуем повторить AnyDesk на Паскале. [1] Анонимность и приватность 2
V VNC продам/Sell VNC Доступы: RDP, VPS, SQL inj, базы, сайты, shell's 1
H Free VNC Раздачи и сливы 0
R RDP4YOU: Все о дедиках для тебя | RDP, SSH, VNC Доступы: RDP, VPS, SQL inj, базы, сайты, shell's 0
M VNC USA Доступы: RDP, VPS, SQL inj, базы, сайты, shell's 0
A Дедики под любые нужды RDP VNC туннели ssh Доступы: RDP, VPS, SQL inj, базы, сайты, shell's 1
S Взламываем RDP,SSH,VNC при помощи Ncrack Уязвимости и взлом 0
Admin Захват VNC сессии на удаленном компьютере. Уязвимости и взлом 1
Admin Интересно Reverse shell через ICMP. Опубликован руткит под лицензией MIT, который обходит все защиты Linux. Новости в сети 0
Admin Статья Управляй компьютером через сотовый! Полезные статьи 0
Admin Статья Поднимаем личный VPN-сервер за 30 мин на своем VPS через Amnezia VPN Анонимность и приватность 0
Admin Статья Поднимаем свой XMPP сервер через i2p в 2025. Полезные статьи 0
Admin Статья Генерируем варианты утерянного пароля через HashCat Полезные статьи 0
Admin Статья Находим бекенд айпи сайта за CDN через дедупликацию OSINT 0
Admin Статья Разбираем инциденты, анализируем honeypots через дашборды для поимки хищников, атакующих нашу инфраструктуру. [Part 2] Анонимность и приватность 0
Admin Статья Деанонимизация через «забытые» утечки OSINT 0
Admin Интересно ИИ стал предателем: LangChain взламывают через ответы самой модели — она сама крадёт ваши секреты. Новости в сети 0
Admin Статья SQL-инъекции: Анатомия атаки и искусство защиты через Prepared Statements Уязвимости и взлом 0
Admin Статья Безопасное использование джаббера через .onion Анонимность и приватность 0
Admin Интересно Через кряки и YouTube распространяют новые загрузчики вредоносов. Новости в сети 0
Admin Интересно Киберпанк в Средиземном море. Французы ищут, какая держава хотела угнать паром через интернет. Новости в сети 0
Admin Интересно А что, так можно было? В системных утилитах FreeBSD нашли уязвимость, позволяющую исполнять чужой код через обычный роутер. Новости в сети 0
Support81 45 тыс фунтов за одно покушение — беглый топ-менеджер Wirecard финансировал шпионов и киллеров через крипту Новости в сети 0
Support81 «Менеджер» с архивом и черным ходом через Yandex. Как группировка APT31 годами шпионила за российскими IT-компаниями Новости в сети 1
Support81 Хакеры захватили 8,7 млн WordPress-сайтов за два дня — атакуют через критические уязвимости в популярных плагинах Новости в сети 0
Support81 Анонимности — конец: депутат Госдумы пообещал, что через три года все действия в российском интернете будут деанонимизированы Новости в сети 3
turbion0 Мошенники похищают аккаунты на «Госуслугах» через объявления Новости в сети 0
Support81 RCE через Game Pass: тысячи ПК взломаны через Call of Duty Новости в сети 0
Support81 Microsoft снова сыграла на руку хакерам — корпоративные сети ломают через официальный софт Новости в сети 0
Support81 SMS для Google и Meta? Перешлём через Намибию, Чечню и швейцарский гараж Новости в сети 0
Support81 JSFireTruck и HelloTDS: новая инфраструктура веб-атак через легитимные домены Новости в сети 0
Support81 Забудьте о вирусах в письмах — теперь ими заражаются через вертикальные видео в TikTok Новости в сети 0
Support81 Миллиарды наркодолларов идут через WeChat — прокуратура вскрыла схему картелей и китайских банков Новости в сети 0
Support81 Seed → POST-запрос → пустой баланс: как устроена быстрая и чистая кража крипты через FreeDrain Новости в сети 0
turbion0 Мошенники выдают фальшивые акции банков в Telegram и воруют деньги через фишинг Новости в сети 0
Support81 Одно касание — и прощайте, деньги: китайская SuperCard X ворует данные карт через NFC-модуль Новости в сети 0
turbion0 Мошенники получают доступ к банковским реквизитам через фейковые сайты. Новости в сети 0
Support81 Хакеры наносят удар через Google Ads: юристы — главная цель Новости в сети 0
Support81 StilachiRAT: хищная киберкрыса похищает биткоины и подглядывает за вами через веб-камеру Новости в сети 0
Support81 Яд в коде: злоумышленники заставляют ИИ внедрять бэкдоры через Unicode-символы Новости в сети 0
Support81 «Мир не готов»: эксперты предсказывают появление сверхразума через год Новости в сети 0
Support81 Вирус в заявке: как Zhong Stealer «ломает» компании через службу поддержки Новости в сети 0
Support81 Фейковая «Безопасность»: Telegram-аккаунты снова крадут через сообщения Новости в сети 0
Support81 WantToCry: новое поколение вымогателей атакует через незащищённый SMB Новости в сети 0
wrangler65 Интересно Как сэкономить до 70% на переводах USDT TRC-20 через энергию Полезные статьи 0
Support81 Слежка через смартфоны: новый скандал с участием спецслужб Новости в сети 0
Support81 Обновление Windows спровоцировало волну атак через архивы Новости в сети 0
Support81 Аналитики IBM: хакеры записывают домашние видео через взломанные маршрутизаторы Новости в сети 0
Support81 Разоблачена тайная программа слежки через мобильные приложения Новости в сети 0

Название темы