Advertisement
Guest User

Untitled

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