Bind to user-specified address (BindAddress)
[mgsmtp.git] / Network.pas
index cd13cb2ae4740ec5368c70686d381395fbd14660..d19aeeacbda844998fea48c0701f40c7cf56afeb 100644 (file)
@@ -54,15 +54,16 @@ type
    TTCPConnection = class
       constructor Create; overload;
       constructor Create(const HostName: string; Port: word); overload;
-      constructor Create(Socket: socket; const Addr: TSockAddr); overload;
+      constructor Create(Socket: socket); overload;
       destructor Destroy; override;
    private
       FConnected: boolean;
       FSocket: socket;
       FHostIP: TIPNamePair;
       FSockTimeOut: DWord;
-      SockAddr: TSockAddr;
+      SrcSockAddr, DstSockAddr: TSockAddr;
    public
+      function SetBindAddress(const HostName: string): boolean;
       function Connect(const HostName: string; Port: word): boolean;
       procedure Disconnect;
       procedure ReverseDNSLookup;
@@ -129,6 +130,8 @@ begin
    FConnected:= false;
    FSocket:= -1;
    FSockTimeOut:= DEF_SOCK_TIMEOUT;
+   FillChar(SrcSockAddr, SizeOf(SrcSockAddr), 0);
+   FillChar(DstSockAddr, SizeOf(DstSockAddr), 0);
 end;
 
 constructor TTCPConnection.Create(const HostName: string; Port: word);
@@ -138,13 +141,17 @@ begin
    Connect(HostName, Port);
 end;
 
-constructor TTCPConnection.Create(Socket: socket; const Addr: TSockAddr);
+constructor TTCPConnection.Create(Socket: socket);
 { Use an already connected socket. }
+var ssocklen, dsocklen: TSockLen;
 begin
    inherited Create;
    FSocket:= Socket;
-   SockAddr:= Addr;
-   FHostIP:= TIPNamePair.Create('', NetAddrToStr(Addr.sin_addr));
+   ssocklen:= SizeOf(SrcSockAddr);
+   dsocklen:= SizeOf(DstSockAddr);
+   fpgetsockname(FSocket, @SrcSockAddr, @ssocklen);
+   fpgetpeername(FSocket, @DstSockAddr, @dsocklen);
+   FHostIP:= TIPNamePair.Create('', NetAddrToStr(DstSockAddr.sin_addr));
    FConnected:= true;
 end;
 
@@ -176,29 +183,44 @@ begin
 end;
 
 
+function TTCPConnection.SetBindAddress(const HostName: string): boolean;
+var GAIResult: TGAIResult;
+begin
+   GAIResult:= ResolveHost(HostName);
+   if GAIResult.GAIError = 0 then begin
+      SrcSockAddr:= GAIResult.AddrInfo^.ai_addr^;
+      FreeHost(GAIResult);
+      Result:= true;
+   end
+   else
+      Result:= false;
+end;
+
 function TTCPConnection.Connect(const HostName: string; Port: word): boolean;
 { Resolves the given hostname, and tries to connect it on the given port. }
 var GAIResult: TGAIResult;
 begin
    FSocket:= fpSocket(af_inet, sock_stream, 0);
    if (FSocket <> -1) then begin
-      GAIResult:= ResolveHost(HostName);
-         if GAIResult.GAIError = 0 then begin
-            SockAddr:= GAIResult.AddrInfo^.ai_addr^;
-                SockAddr.sin_port:= htons(Port);
-
-                if SockAddr.sin_addr.s_addr <> 0 then
-                   { Try to initiate connection. }
-                   FConnected:= fpConnect(FSocket, @SockAddr, SizeOf(SockAddr)) <> -1;
-
-                if FConnected then begin
-                   FHostIP:= TIPNamePair.Create(HostName, NetAddrToStr(SockAddr.sin_addr));
-                   SetSockTimeOut(FSockTimeOut);
-                end
-                else
-                   CloseSocket(FSocket);
+      if (SrcSockAddr.sin_addr.s_addr = 0) or (fpBind(FSocket, @SrcSockAddr, SizeOf(SrcSockAddr)) = 0) then begin
+         GAIResult:= ResolveHost(HostName);
+         if GAIResult.GAIError = 0 then begin
+            DstSockAddr:= GAIResult.AddrInfo^.ai_addr^;
+            DstSockAddr.sin_port:= htons(Port);
+
+            if DstSockAddr.sin_addr.s_addr <> 0 then
+            { Try to initiate connection. }
+            FConnected:= fpConnect(FSocket, @DstSockAddr, SizeOf(DstSockAddr)) <> -1;
+
+            if FConnected then begin
+               FHostIP:= TIPNamePair.Create(HostName, NetAddrToStr(DstSockAddr.sin_addr));
+               SetSockTimeOut(FSockTimeOut);
+            end
+            else
+               CloseSocket(FSocket);
 
-         FreeHost(GAIResult);
+            FreeHost(GAIResult);
+         end;
       end;
    end;
    Result:= FConnected;
@@ -218,7 +240,7 @@ procedure TTCPConnection.ReverseDNSLookup;
 var NHostIP: TIPNamePair;
 begin
    if FConnected then begin
-      NHostIP:= TIPNamePair.Create(ResolveIP(@SockAddr), FHostIP.IP);
+      NHostIP:= TIPNamePair.Create(ResolveIP(@DstSockAddr), FHostIP.IP);
       FHostIP.Free;
       FHostIP:= NHostIP;
    end;
@@ -356,9 +378,9 @@ begin
            connection. }
          case FFeatureRequest of
          NET_TCP_BASIC:
-            TCPConnection:= TTCPConnection.Create(ClientSocket, SockAddr);
+            TCPConnection:= TTCPConnection.Create(ClientSocket);
          NET_TCP_RFCSUPPORT:
-            TCPConnection:= TTCPRFCConnection.Create(ClientSocket, SockAddr);
+            TCPConnection:= TTCPRFCConnection.Create(ClientSocket);
          end;
 
          { Then start a new thread with the connection handler. }