본문 바로가기
DELPHI(델파이)

[Delphi] TIdMappedPortTCP를 이용한 허용된 ip만 추가하는 포트포워딩

by Jcoder 2020. 12. 4.

설정 폼

1. 서비스

  THanilnPortFowarding_Service = class(TService)

    CryptographicLibrary1 : TCryptographicLibrary;

    Codec1 : TCodec;

    procedure ServiceCreate(Sender : TObject);

    procedure ServiceStart(Sender : TService; var Started : Boolean);

    procedure ServiceStop(Sender : TService; var Stopped : Boolean);

    procedure ServicePause(Sender : TService; var Paused : Boolean);

    procedure ServiceContinue(Sender : TService; var Continued : Boolean);

    procedure ServiceDestroy(Sender : TObject);

  private

    AllowIpList : TList<string>; // 허용 IP 리스트

    FowardingList : TList<TFowardingClass>; // 포트포워딩할 IP, PORT 리스트<TFowardingClass>

    { Private declarations }

    procedure AddAllowIpList(s : string); // 허용 IP 리스트 추가

    procedure LoadIniFile// 암호회된 INI 읽어오기

  public

    function GetServiceController : TServiceController; override;

    { Public declarations }

  end;

 

 

unit uMappedTCPClass;

 

interface

 

uses

  System.SysUtils, IdContext, IdSocketHandle, IdThread, IdComponent, IdBaseComponent, IdCustomTCPServer, IdServerIOHandler,

  IdServerIOHandlerSocket, IdServerIOHandlerStack, System.RegularExpressions, System.Generics.Collections, IdMappedPortTCP, uLogClass,

  IdSSL, IdSSLOpenSSL, IdIntercept, IdServerInterceptLogBase, IdServerInterceptLogFile, Vcl.Forms;

 

type

  TFowardingClass = class

    IdMappedPortTCP : TIdMappedPortTCP;

    IdServerIOHandlerSSLOpenSSL : TIdServerIOHandlerSSLOpenSSL;

    IdServerInterceptLogFile : TIdServerInterceptLogFile;

    procedure IdMappedPortTCP1AfterBind(Sender : TObject);

    procedure IdMappedPortTCP1BeforeBind(AHandle : TIdSocketHandle);

    procedure IdMappedPortTCP1BeforeConnect(AContext : TIdContext);

    procedure IdMappedPortTCP1BeforeListenerRun(AThread : TIdThread);

    procedure IdMappedPortTCP1Connect(AContext : TIdContext);

    procedure IdMappedPortTCP1Disconnect(AContext : TIdContext);

    procedure IdMappedPortTCP1Exception(AContext : TIdContext; AException : Exception);

  private

    BindIP : string;

    BindPort : integer;

    MappedIP : string;

    MappedPort : integer;

    sPath : string;

    AllowIpList : TList<string>; // 허용 IP 리스트

    function IsAllowIp(sip : string) : Boolean// 허용된 IP 리스트에 있는지 확인

  protected

    { protected declarations }

    procedure Connect;

    procedure Disconnect;

  public

    { public declarations }

    constructor Create(sBindIP, sBindPort, sMappedIP, sMappedPort : string; sAllowIpList : TList<string>);

    destructor Destroy;

  end;

 

const

  ipRegExp =

    '\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b';

 

implementation

 

{ TFowardingClass }

 

procedure TFowardingClass.Connect;

begin

  IdMappedPortTCP.Active := true;

end;

 

constructor TFowardingClass.Create(sBindIP, sBindPort, sMappedIP, sMappedPort : string; sAllowIpList : TList<string>);

begin

  BindIP     := sBindIP;

  BindPort   := sBindPort.ToInteger;

  MappedIP   := sMappedIP;

  MappedPort := sMappedPort.ToInteger;

  // IPv4 체크

  if not(TRegEx.IsMatch(BindIP, ipRegExp) or not TRegEx.IsMatch(MappedIP, ipRegExp)) then

  begin

    free;

  end;

  // 포트 범위 체크

  if not((BindPort > 0and (BindPort < 65536)) or not((MappedPort > 0and (MappedPort < 65536)) then

  begin

    free;

  end;

 

  AllowIpList                                   := sAllowIpList; // 넘겨준 리스트 담기 (이미 create 했기 때문에 얕은 복사

  IdMappedPortTCP                               := TIdMappedPortTCP.Create; // IdMappedPortTCP Create

  IdServerIOHandlerSSLOpenSSL                   := TIdServerIOHandlerSSLOpenSSL.Create;

  IdServerIOHandlerSSLOpenSSL.SSLOptions.Method := sslvTLSv1_2;

 

  try

    IdMappedPortTCP.Intercept := IdServerInterceptLogFile;

    IdMappedPortTCP.IOHandler := IdServerIOHandlerSSLOpenSSL;

    // 이벤트 연결

    IdMappedPortTCP.OnAfterBind          := IdMappedPortTCP1AfterBind;

    IdMappedPortTCP.OnBeforeBind         := IdMappedPortTCP1BeforeBind;

    IdMappedPortTCP.OnBeforeConnect      := IdMappedPortTCP1BeforeConnect;

    IdMappedPortTCP.OnBeforeListenerRun  := IdMappedPortTCP1BeforeListenerRun;

    IdMappedPortTCP.OnConnect            := IdMappedPortTCP1Connect;

    IdMappedPortTCP.OnDisconnect         := IdMappedPortTCP1Disconnect;

    IdMappedPortTCP.OnException          := IdMappedPortTCP1Exception;

 

    sPath := StringReplace(BindIP + '.' + BindPort.ToString, '.''_', [rfReplaceAll]);

 

    with IdMappedPortTCP do

    begin

      Bindings.Clear;

      defaultport       := 0;

      Bindings.Add.IP   := BindIP;

      Bindings.Add.port := BindPort;

      mappedHost        := MappedIP;

    end;

    IdMappedPortTCP.MappedPort := MappedPort;

    Connect;

  except

    on E : Exception do

      WriteLog('FowardingClass - Exception : ' + E.Message);

  end;

end;

 

destructor TFowardingClass.Destroy;

begin

  inherited;

  Disconnect;

 

  if assigned(IdServerIOHandlerSSLOpenSSL) then

    IdServerIOHandlerSSLOpenSSL.free;

 

  if assigned(IdMappedPortTCP) then

    IdMappedPortTCP.free;

 

  if assigned(AllowIpList) then

    AllowIpList.free;

end;

 

procedure TFowardingClass.Disconnect;

begin

  IdMappedPortTCP.Active := False;

end;

 

procedure TFowardingClass.IdMappedPortTCP1AfterBind(Sender : TObject);

begin

  WriteLog('AfterBind', sPath);

end;

 

procedure TFowardingClass.IdMappedPortTCP1BeforeBind(AHandle : TIdSocketHandle);

begin

  WriteLog('BeforeBind', sPath);

end;

 

procedure TFowardingClass.IdMappedPortTCP1BeforeConnect(AContext : TIdContext);

begin

  // 허용된 ip인지 체크

  if not IsAllowIp(AContext.Binding.PeerIP) then

  begin

    // 허용된 ip가 아니면 연결해제

    AContext.Connection.Disconnect;

    WriteLog(format('BeforeConnect Disconnect - Not Allow IP : %s, Port : %d', [AContext.Binding.PeerIP, AContext.Binding.PeerPort]

      ), sPath);

  end

  else

  begin

    WriteLog(format('BeforeConnect - Allow IP : %s, Port : %d', [AContext.Binding.PeerIP, AContext.Binding.PeerPort]), sPath);

  end;

end;

 

procedure TFowardingClass.IdMappedPortTCP1BeforeListenerRun(AThread : TIdThread);

begin

  WriteLog('BeforeListenerRun', sPath);

end;

 

procedure TFowardingClass.IdMappedPortTCP1Connect(AContext : TIdContext);

begin

  WriteLog(format('Connect -  IP : %s, Port : %d', [AContext.Binding.IP, AContext.Binding.port]), sPath);

  WriteLog(format('Connect -  PeerIP : %s, PeerPort : %d', [AContext.Binding.PeerIP, AContext.Binding.PeerPort]), sPath);

end;

 

procedure TFowardingClass.IdMappedPortTCP1Disconnect(AContext : TIdContext);

begin

  WriteLog(format('Disconnect -  IP : %s, Port : %d', [AContext.Binding.IP, AContext.Binding.port]), sPath);

  WriteLog(format('Disconnect -  PeerIP : %s, PeerPort : %d', [AContext.Binding.PeerIP, AContext.Binding.PeerPort]), sPath);

end;

 

procedure TFowardingClass.IdMappedPortTCP1Exception(AContext : TIdContext; AException : Exception);

begin

  WriteLog(format('Exception -  IP : %s, Port : %d', [AContext.Binding.IP, AContext.Binding.port]), sPath);

  WriteLog(format('Exception -  PeerIP : %s, PeerPort : %d', [AContext.Binding.PeerIP, AContext.Binding.PeerPort]), sPath);

  WriteLog(format('Exception - AException : %s, StackTrace : %s', [AException.Message, AException.StackTrace]), sPath);

end;



function TFowardingClass.IsAllowIp(sip : string) : Boolean;

begin

  Result := False;

  if AllowIpList.IndexOf(sip) <> -1 then

    Result := true;

end;

 

end.