Bind to user-specified address (BindAddress6) ipv6
authorMegaBrutal <code+git@megabrutal.com>
Tue, 20 Nov 2018 20:14:56 +0000 (21:14 +0100)
committerMegaBrutal <code+git@megabrutal.com>
Fri, 23 Nov 2018 18:15:00 +0000 (19:15 +0100)
Bind to user-specified address for outgoing connections.

modified:   Common.pas
modified:   MgSMTP.pas
modified:   Network.pas
modified:   Relay.pas

Common.pas
MgSMTP.pas
Network.pas
Relay.pas

index 2280c2b5358f968ba6d8f0c5c070e445088be0d3..f7a88542fff0a6ba0160e3f9eabf6d6d326e60ad 100644 (file)
@@ -74,10 +74,13 @@ type
       FTimeOffset: integer;
       FTimeOffsetStr: string;
       FListenAddresses, FListenAddresses6: TStrings;
+      FBindAddress, FBindAddress6: string;
    public
       function GetVersionStr: string;
       property ListenAddresses: TStrings read FListenAddresses;
       property ListenAddresses6: TStrings read FListenAddresses6;
+      property BindAddress: string read FBindAddress;
+      property BindAddress6: string read FBindAddress6;
       property Databytes: longint read FDatabytes;
       {property TimeCorrection: integer read FTimeCorrection;}
       property TimeOffset: integer read FTimeOffset;
@@ -537,6 +540,9 @@ begin
    FListenAddresses6.Delimiter:= ',';
    FListenAddresses6.DelimitedText:= Config.ReadString('Server', 'ListenAddress6', '');
 
+   FBindAddress:=    Config.ReadString('Server', 'BindAddress', '0.0.0.0');
+   FBindAddress6:=   Config.ReadString('Server', 'BindAddress6', '[::]');
+
    FDatabytes:=      Config.ReadInteger('Server', 'Databytes', 1024 * 1024 * 1024);
    FTimeOffset:=     Config.ReadInteger('Server', 'TimeOffset', Config.ReadInteger('Server', 'TimeCorrection', 0) * 100);
    FTimeOffsetStr:=  MakeTimeOffsetStr(FTimeOffset);
index 154ad6344f3424d87e9e9f92deeb9dd1fae9b1b6..f7a8447bc1ee6fcf5186f37c24d4fb44375434d3 100644 (file)
@@ -49,7 +49,7 @@ const
      document what bugfix/feature are you testing with the actual build.
      This will be logged to help you differentiate outputs of subsequent
      builds in your logs. If left empty, it won't be added to the logs. }
-   DEVCOMMENT  =  'IPv6 listen addresses (ListenAddress6)';
+   DEVCOMMENT  =  'BindAddress6';
 
 var
 
index ec602d1a8995022c6c12ec3a21766d988d49f037..a28ff634077b64e0a77e8c36be148ead9d515017 100644 (file)
@@ -37,7 +37,7 @@
 unit Network;
 
 interface
-uses Classes, Sockets, SocketUtils, DNSResolve, NetRFC, Common;
+uses Classes, Sockets, SocketUtils, ctypes, DNSResolve, NetRFC, Common;
 
 const
 
@@ -61,15 +61,21 @@ type
    TTCPConnection = class
       constructor Create; overload;
       constructor Create(const HostName: string; Port: word); overload;
-      constructor Create(Socket: socket; const Addr: TSockAddr6); overload;
+      constructor Create(Socket: socket); overload;
       destructor Destroy; override;
    private
       FConnected: boolean;
       FSocket: socket;
       FHostIP: TIPNamePair;
       FSockTimeOut: DWord;
-      SockAddr: TSockAddr6;
+      SrcSockAddr: TSockAddr;
+      SrcSockAddr6: TSockAddr6;
+      DstSockAddr: TSockAddr6;
+   protected
+      function IsNullAddress(SockAddr: PSockAddr): boolean;
+      function BindSrcAddr(Socket: socket; Family: word): cint;
    public
+      function SetBindAddress(Family: word; const HostName: string): boolean;
       function Connect(const HostName: string; Port: word): boolean;
       procedure Disconnect;
       procedure ReverseDNSLookup;
@@ -136,6 +142,11 @@ begin
    FConnected:= false;
    FSocket:= -1;
    FSockTimeOut:= DEF_SOCK_TIMEOUT;
+   FillChar(SrcSockAddr, SizeOf(SrcSockAddr), 0);
+   FillChar(SrcSockAddr6, SizeOf(SrcSockAddr6), 0);
+   FillChar(DstSockAddr, SizeOf(DstSockAddr), 0);
+   SrcSockAddr.sin_family:= AF_INET;
+   SrcSockAddr6.sin6_family:= AF_INET6;
 end;
 
 constructor TTCPConnection.Create(const HostName: string; Port: word);
@@ -145,13 +156,17 @@ begin
    Connect(HostName, Port);
 end;
 
-constructor TTCPConnection.Create(Socket: socket; const Addr: TSockAddr6);
+constructor TTCPConnection.Create(Socket: socket);
 { Use an already connected socket. }
+var ssocklen, dsocklen: TSockLen;
 begin
    inherited Create;
    FSocket:= Socket;
-   SockAddr:= Addr;
-   FHostIP:= TIPNamePair.Create('', IPToStr(@Addr));
+   ssocklen:= SizeOf(SrcSockAddr);
+   dsocklen:= SizeOf(DstSockAddr);
+   fpgetsockname(FSocket, @SrcSockAddr, @ssocklen);
+   fpgetpeername(FSocket, @DstSockAddr, @dsocklen);
+   FHostIP:= TIPNamePair.Create('', IPToStr(@DstSockAddr));
    FConnected:= true;
 end;
 
@@ -184,26 +199,84 @@ begin
 end;
 
 
+function TTCPConnection.IsNullAddress(SockAddr: PSockAddr): boolean;
+begin
+   if SockAddr^.sin_family = AF_INET then
+      Result:= SockAddr^.sin_addr.s_addr = 0
+   else if SockAddr^.sin_family = AF_INET6 then
+      Result:= (PSockAddr6(SockAddr)^.sin6_addr.u6_addr32[0] = 0)
+         and (PSockAddr6(SockAddr)^.sin6_addr.u6_addr32[1] = 0)
+         and (PSockAddr6(SockAddr)^.sin6_addr.u6_addr32[2] = 0)
+         and (PSockAddr6(SockAddr)^.sin6_addr.u6_addr32[3] = 0)
+   else
+      Result:= true;
+end;
+
+function TTCPConnection.BindSrcAddr(Socket: socket; Family: word): cint;
+var SockAddr: PSockAddr; addrlen: size_t;
+begin
+   case Family of
+      AF_INET:
+         begin
+            SockAddr:= @SrcSockAddr;
+            addrlen:= SizeOf(SrcSockAddr);
+         end;
+      AF_INET6:
+         begin
+            SockAddr:= @SrcSockAddr6;
+            addrlen:= SizeOf(SrcSockAddr6);
+         end;
+   end;
+
+   if not IsNullAddress(SockAddr) then
+      Result:= fpBind(Socket, SockAddr, addrlen)
+   else
+      Result:= 0;
+end;
+
+function TTCPConnection.SetBindAddress(Family: word; const HostName: string): boolean;
+var GAIResult: TGAIResult; SockAddr: PSockAddr;
+begin
+   GAIResult:= ResolveHost(HostName, Family);
+   if GAIResult.GAIError = 0 then begin
+      case GAIResult.AddrInfo^.ai_family of
+         AF_INET:    SockAddr:= @SrcSockAddr;
+         AF_INET6:   SockAddr:= @SrcSockAddr6;
+      end;
+      Move(GAIResult.AddrInfo^.ai_addr^, SockAddr^, GAIResult.AddrInfo^.ai_addrlen);
+      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
    GAIResult:= ResolveHost(HostName, AF_UNSPEC);
    if GAIResult.GAIError = 0 then begin
-      Move(GAIResult.AddrInfo^.ai_addr^, SockAddr, GAIResult.AddrInfo^.ai_addrlen);
-      SockAddr.sin6_port:= htons(Port);
+      Move(GAIResult.AddrInfo^.ai_addr^, DstSockAddr, GAIResult.AddrInfo^.ai_addrlen);
+      DstSockAddr.sin6_port:= htons(Port);
 
       { Create socket. }
       FSocket:= fpSocket(GAIResult.AddrInfo^.ai_family, SOCK_STREAM, 0);
 
       if (FSocket <> -1) then begin
 
-         { Try to initiate connection. }
-         FConnected:= fpConnect(FSocket, @SockAddr, GAIResult.AddrInfo^.ai_addrlen) <> -1;
+         if BindSrcAddr(FSocket, GAIResult.AddrInfo^.ai_family) = 0 then begin
+
+            { Try to initiate connection. }
+            FConnected:= fpConnect(FSocket, @DstSockAddr, GAIResult.AddrInfo^.ai_addrlen) <> -1;
+
+            if FConnected then begin
+               FHostIP:= TIPNamePair.Create(HostName, IPToStr(@DstSockAddr));
+               SetSockTimeOut(FSockTimeOut);
+            end
+            else
+               CloseSocket(FSocket);
 
-         if FConnected then begin
-            FHostIP:= TIPNamePair.Create(HostName, IPToStr(@SockAddr));
-            SetSockTimeOut(FSockTimeOut);
          end
          else
             CloseSocket(FSocket);
@@ -229,7 +302,7 @@ procedure TTCPConnection.ReverseDNSLookup;
 var NHostIP: TIPNamePair;
 begin
    if FConnected then begin
-      NHostIP:= TIPNamePair.Create(ResolveIP(PSockAddr(@SockAddr)), FHostIP.IP);
+      NHostIP:= TIPNamePair.Create(ResolveIP(PSockAddr(@DstSockAddr)), FHostIP.IP);
       FHostIP.Free;
       FHostIP:= NHostIP;
    end;
@@ -362,9 +435,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. }
index cf3876027bef390a883e18f7771258ceced65416..e863023fde92a7f11ca9eadb8476511ddb0f1749 100644 (file)
--- a/Relay.pas
+++ b/Relay.pas
@@ -1,6 +1,6 @@
 {
    MegaBrutal's SMTP Server (MgSMTP)
-   Copyright (C) 2010-2015 MegaBrutal
+   Copyright (C) 2010-2018 MegaBrutal
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
@@ -331,7 +331,10 @@ var MXList: TStrings; i: integer;
 begin
    MXList:= GetCorrectMXRecordList(RelayServerName);
    if MXList.Count >= 1 then begin
-      TCP:= TTCPRFCConnection.Create(MXList.Strings[0], RelayServerPort);
+      TCP:= TTCPRFCConnection.Create;
+      TCP.SetBindAddress(AF_INET,  MainServerConfig.BindAddress);
+      TCP.SetBindAddress(AF_INET6, MainServerConfig.BindAddress6);
+      TCP.Connect(MXList.Strings[0], RelayServerPort);
       TCP.SetSockTimeOut(DEF_SOCK_TIMEOUT);
       i:= 1;
       while (not TCP.Connected) and (i < MXList.Count) do begin