Advertisement
Guest User

Untitled

a guest
Jan 22nd, 2024
67
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Delphi 9.93 KB | None | 0 0
  1. unit osAPITCPEngineServer;
  2.  
  3. interface
  4.  
  5. uses
  6.   IdCTypes,
  7.   IdContext,
  8.   IdComponent,
  9.  
  10.   IdTCPServer,
  11.  
  12.   IdSSLOpenSSL,
  13.   IdSSLOpenSSLHeaders,
  14.  
  15.   osAPITCPServerContext,
  16.   osServiceServersTypes;
  17.  
  18. type
  19.   TAPITCPEngineServer = class
  20.   private
  21.     FCertificatesPath: string;
  22.     FCmdCommandsBlockEnabled: Boolean;
  23.     FIOHandlerSSLOpenSLL: TIdServerIOHandlerSSLOpenSSL;
  24.     FListenPort: Word;
  25.     FOpenSSLDLLPath: string;
  26.     FSecurityPolicy: TConnectionSecurityPolicy;
  27.     FServer: TIdTCPServer;
  28.     FSetCommandsBlockEnabled: Boolean;
  29.     FStopNewConnections: Boolean;
  30.   private
  31.     function GetActive: Boolean;
  32.   private
  33.     procedure SetCertificatesPath(const Value: string);
  34.     procedure SetListenPort(Value: Word);
  35.     procedure SetOpenSSLDLLPath(const Value: string);
  36.   private
  37.     procedure ServerConnect(AContext: TIdContext);
  38.     procedure ServerExecute(AContext: TIdContext);
  39.   private
  40.     procedure SSLGetPassword(var Password: string);
  41.     procedure SSLGetPasswordEx(ASender: TObject; var VPassword: string; const AIsWrite: Boolean);
  42.     procedure SSLStatus(ASender: TObject; const AStatus: TIdStatus; const AStatusText: string);
  43.     procedure SSLStatusInfo(const AMsg: string);
  44.     procedure SSLStatusInfoEx(ASender: TObject; const AsslSocket: PSSL; const AWhere, Aret: TIdC_INT; const AType, AMsg: string);
  45.   public
  46.     constructor Create;
  47.     destructor Destroy; override;
  48.   public
  49.     function Start: Boolean;
  50.     function Stop: Boolean;
  51.   public
  52.     property Active: Boolean read GetActive;
  53.     property CertificatesPath: string read FCertificatesPath write SetCertificatesPath;
  54.     property CmdCommandsBlockEnabled: Boolean read FCmdCommandsBlockEnabled write FCmdCommandsBlockEnabled;
  55.     property ListenPort: Word read FListenPort write SetListenPort;
  56.     property OpenSSLDLLPath: string read FOpenSSLDLLPath write SetOpenSSLDLLPath;
  57.     property SecurityPolicy: TConnectionSecurityPolicy read FSecurityPolicy write FSecurityPolicy;
  58.     property SetCommandsBlockEnabled: Boolean read FSetCommandsBlockEnabled write FSetCommandsBlockEnabled;
  59.   end;
  60.  
  61. implementation
  62.  
  63. uses
  64.   System.Classes,
  65.  
  66.   IdGlobal,
  67.  
  68.   osSysUtils;
  69.  
  70. const
  71.   { default constants }
  72.   DEF_LISTEN_PORT     = 8000;
  73.   DEF_SECURITY_POLICY = cnsp_None;
  74.  
  75. { TAPITCPEngineServer }
  76.  
  77. constructor TAPITCPEngineServer.Create;
  78. begin
  79.   // sets default members values
  80.   FCertificatesPath := '';
  81.   FCmdCommandsBlockEnabled := False;
  82.   FIOHandlerSSLOpenSLL := nil;
  83.   FListenPort := DEF_LISTEN_PORT;
  84.   FOpenSSLDLLPath := '';
  85.   FSecurityPolicy := DEF_SECURITY_POLICY;
  86.   FServer := nil;
  87.   FSetCommandsBlockEnabled := False;
  88.   FStopNewConnections := False;
  89. end;
  90.  
  91. destructor TAPITCPEngineServer.Destroy;
  92. begin
  93.   // frees objects
  94.   SafeFreeAndNil(FServer);
  95.   SafeFreeAndNil(FIOHandlerSSLOpenSLL);
  96.  
  97.   inherited;
  98. end;
  99.  
  100. function TAPITCPEngineServer.GetActive: Boolean;
  101. begin
  102.   if FServer = nil then Exit(False);
  103.   Result := FServer.Active;
  104. end;
  105.  
  106. procedure TAPITCPEngineServer.ServerConnect(AContext: TIdContext);
  107. begin
  108.   // checks if new connections are permitted
  109.   if FStopNewConnections then
  110.     AContext.Connection.Disconnect;
  111.  
  112.   // ###
  113.   if not (AContext.Connection.IOHandler is TIdSSLIOHandlerSocketOpenSSL) then
  114.   begin
  115.     AContext.Connection.Disconnect;
  116.     Exit;
  117.   end;
  118. end;
  119.  
  120. procedure TAPITCPEngineServer.ServerExecute(AContext: TIdContext);
  121. begin
  122.   // enables server request to be UTF8 compliant
  123.   AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
  124.   TAPITCPServerContext(AContext).CmdCommandsBlockEnabled := FCmdCommandsBlockEnabled;
  125.   TAPITCPServerContext(AContext).SetCommandsBlockEnabled := FSetCommandsBlockEnabled;
  126.   TAPITCPServerContext(AContext).Execute;
  127. end;
  128.  
  129. procedure TAPITCPEngineServer.SetCertificatesPath(const Value: string);
  130. begin
  131.   if (FServer <> nil) and FServer.Active then Exit;
  132.   FCertificatesPath := Value;
  133. end;
  134.  
  135. procedure TAPITCPEngineServer.SetListenPort(Value: Word);
  136. begin
  137.   if (FServer <> nil) and FServer.Active then Exit;
  138.   FListenPort := Value;
  139. end;
  140.  
  141. procedure TAPITCPEngineServer.SetOpenSSLDLLPath(const Value: string);
  142. begin
  143.   if (FServer <> nil) and FServer.Active then Exit;
  144.   FOpenSSLDLLPath := Value;
  145. end;
  146.  
  147. procedure TAPITCPEngineServer.SSLGetPassword(var Password: string);
  148. begin
  149.   //### TODO: Used for debug. At moment it is never called at client connection!!!
  150.   Password := '';
  151. end;
  152.  
  153. procedure TAPITCPEngineServer.SSLGetPasswordEx(ASender: TObject; var VPassword: string; const AIsWrite: Boolean);
  154. begin
  155.   //### TODO: Used for debug. At moment it is never called at client connection!!!
  156. end;
  157.  
  158. procedure TAPITCPEngineServer.SSLStatus(ASender: TObject; const AStatus: TIdStatus; const AStatusText: string);
  159. begin
  160.   //### TODO: Used for debug. At moment it is never called at client connection!!!
  161. end;
  162.  
  163. procedure TAPITCPEngineServer.SSLStatusInfo(const AMsg: string);
  164. begin
  165.   //### TODO: Used for debug. At moment it is never called at client connection!!!
  166. end;
  167.  
  168. procedure TAPITCPEngineServer.SSLStatusInfoEx(ASender: TObject; const AsslSocket: PSSL; const AWhere, Aret: TIdC_INT; const AType, AMsg: string);
  169. begin
  170.   //### TODO: Used for debug. At moment it is never called at client connection!!!
  171. end;
  172.  
  173. function TAPITCPEngineServer.Start: Boolean;
  174. begin
  175.   try
  176.     if (FServer <> nil) and (FServer.Active) then Exit(True);
  177.     if FServer = nil then
  178.     begin
  179.       FStopNewConnections := False;
  180.  
  181.       // creates and sets tcp server
  182.       FServer := TIdTCPServer.Create(nil);
  183.       FServer.Active := False;
  184.       FServer.ContextClass := TAPITCPServerContext;
  185.       FServer.DefaultPort := FListenPort;
  186.       FServer.OnConnect := ServerConnect;
  187.       FServer.OnExecute := ServerExecute;
  188.  
  189.       // sets OpenSLL library path
  190.       IdOpenSSLSetLibPath(FOpenSSLDLLPath);
  191.  
  192.       // evaluates security policy
  193.       case FSecurityPolicy of
  194.         cnsp_None: ;
  195.  
  196.         cnsp_sslvTLSv1_2:
  197.         begin
  198.           FIOHandlerSSLOpenSLL := TIdServerIOHandlerSSLOpenSSL.Create(nil);
  199.           FIOHandlerSSLOpenSLL.OnStatus := SSLStatus;
  200.           FIOHandlerSSLOpenSLL.OnStatusInfo := SSLStatusInfo;
  201.           FIOHandlerSSLOpenSLL.OnStatusInfoEx := SSLStatusInfoEx;
  202.           FIOHandlerSSLOpenSLL.OnGetPassword := SSLGetPassword;
  203.           FIOHandlerSSLOpenSLL.OnGetPasswordEx := SSLGetPasswordEx;
  204.  
  205.           FIOHandlerSSLOpenSLL.SSLOptions.Mode := sslmServer;
  206.           FIOHandlerSSLOpenSLL.SSLOptions.Method := sslvTLSv1_2;
  207.           FIOHandlerSSLOpenSLL.SSLOptions.SSLVersions := [sslvTLSv1_2];
  208.           FIOHandlerSSLOpenSLL.SSLOptions.DHParamsFile := '';
  209.           FIOHandlerSSLOpenSLL.SSLOptions.CertFile := FCertificatesPath + 'server-cert.pem';
  210.           FIOHandlerSSLOpenSLL.SSLOptions.KeyFile := FCertificatesPath + 'server-key.pem';
  211. //        FIOHandlerSSLOpenSLL.SSLOptions.RootCertFile := FCertificatesPath + 'root-cert.pem';
  212.           FIOHandlerSSLOpenSLL.SSLOptions.RootCertFile := '';
  213.           FIOHandlerSSLOpenSLL.SSLOptions.CipherList := '!EXPORT:!LOW:!aNULL:!eNULL:!RC4:!ADK:!3DES:!DES:!MD5:!PSK:!SRP:!CAMELLIA'+
  214.                       ':ECDHE-RSA-AES128-GCM-SHA256'+
  215.                       ':ECDHE-RSA-AES256-GCM-SHA384'+
  216.                       ':ECDHE-RSA-CHACHA20-POLY1305'+
  217.                       //to use this two you must create a dhparam.pem file with openssl in this way
  218.                       //openssl dhparam -out dhparam.pem 4096
  219.                       //':DHE-RSA-AES128-GCM-SHA256'+
  220.                       //':DHE-RSA-AES256-GCM-SHA384'+
  221.                       '';
  222.           FServer.IOHandler := FIOHandlerSSLOpenSLL;
  223.         end;
  224.       end;
  225.     end;
  226.     FServer.Active := True;
  227.     Result := FServer.Active;
  228.   except
  229.     Result := False;
  230.   end;
  231. end;
  232.  
  233. function TAPITCPEngineServer.Stop: Boolean;
  234.  
  235.   function ActivitiesStop: Boolean;
  236.   var
  237.     I: Integer;
  238.     LList: TList;
  239.     Context: TAPITCPServerContext;
  240.   begin
  241.     Result := False;
  242.     LList := FServer.Contexts.LockList;
  243.     try
  244.       for I := 0 to LList.Count - 1 do
  245.       begin
  246.         Context := TAPITCPServerContext(LList[I]);
  247.         Context.ActivitiesStop := True;
  248.         Result := True;
  249.       end;
  250.     finally
  251.       FServer.Contexts.UnlockList;
  252.     end;
  253.   end;
  254.  
  255.   function ActivitiesStoppedCheck: Boolean;
  256.   var
  257.     I: Integer;
  258.     LList: TList;
  259.     Context: TAPITCPServerContext;
  260.   begin
  261.     Result := False;
  262.     LList := FServer.Contexts.LockList;
  263.     try
  264.       for I := 0 to LList.Count - 1 do
  265.       begin
  266.         Context := TAPITCPServerContext(LList[I]);
  267.         Result := not Context.ActivitiesStopped;
  268.         if Result then Exit;
  269.       end;
  270.     finally
  271.       FServer.Contexts.UnlockList;
  272.     end;
  273.   end;
  274.  
  275. begin
  276.   Result := True;
  277.   try
  278.     if FServer = nil then Exit;
  279.     if FServer.Active then
  280.     begin
  281.       {**
  282.        *  TAKE CARE
  283.        *  =========
  284.        *  At this point we are in primary thread and we want to close all API Server connections but someone could be
  285.        *  in waiting for Thread.Syncronize and stay in infinite loop because TApplication.Idle is not managed, and the
  286.        *  related CheckSynchronize will never be called, we are in primary thread here.
  287.        *  A valid solution, before to set server Active to False is:
  288.        *  - Refuse any new connection on OnConnect with FStopNewConnections to True.
  289.        *  - Ask clients context to stop any activity (get request and put a response which could call TThread.Synchronize.
  290.        *  - Check when all client context accepted the stop to any activity.
  291.        *    In the meanwhile call CheckSynchronize to manage possible pending TThread.Synchronize calls.
  292.        *
  293.        **}
  294.       FStopNewConnections := True;
  295.       if ActivitiesStop then
  296.       begin
  297.         while ActivitiesStoppedCheck do
  298.         begin
  299.           CheckSynchronize;
  300.           TThread.Sleep(1);
  301.         end;
  302.       end;
  303.       FServer.Active := False;
  304.     end;
  305.  
  306.     // frees objects
  307.     SafeFreeAndNil(FServer);
  308.     SafeFreeAndNil(FIOHandlerSSLOpenSLL);
  309.   except
  310.     Result := False;
  311.   end;
  312. end;
  313.  
  314. end.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement