From 7c2747623547f74cab3bdb565b24edbd81fef086 Mon Sep 17 00:00:00 2001 From: MegaBrutal Date: Tue, 20 Nov 2018 21:14:56 +0100 Subject: [PATCH] Bind to user-specified address (BindAddress6) Bind to user-specified address for outgoing connections. modified: Common.pas modified: MgSMTP.pas modified: Network.pas modified: Relay.pas --- Common.pas | 6 +++ MgSMTP.pas | 2 +- Network.pas | 105 ++++++++++++++++++++++++++++++++++++++++++++-------- Relay.pas | 7 +++- 4 files changed, 101 insertions(+), 19 deletions(-) diff --git a/Common.pas b/Common.pas index 2280c2b..f7a8854 100644 --- a/Common.pas +++ b/Common.pas @@ -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); diff --git a/MgSMTP.pas b/MgSMTP.pas index 154ad63..f7a8447 100644 --- a/MgSMTP.pas +++ b/MgSMTP.pas @@ -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 diff --git a/Network.pas b/Network.pas index ec602d1..a28ff63 100644 --- a/Network.pas +++ b/Network.pas @@ -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. } diff --git a/Relay.pas b/Relay.pas index cf38760..e863023 100644 --- 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 -- 2.34.1